-
Notifications
You must be signed in to change notification settings - Fork 62
feat(ai-plugin): implement AI model auto-detection and enhance plugin loading reliability #837
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
61a0116
e45ac15
05cd94d
85a21a6
81359b5
95a5496
6171a02
be856f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,92 @@ | ||
stores: | ||
- name: git | ||
kind: | ||
name: atest-store-git | ||
enabled: true | ||
url: xxx | ||
readonly: false | ||
disabled: false | ||
- name: ai | ||
kind: | ||
name: atest-ext-ai | ||
enabled: true | ||
url: "" | ||
readonly: false | ||
disabled: false | ||
properties: | ||
- name: git | ||
kind: | ||
name: atest-store-git | ||
dependencies: [] | ||
url: "unix:///tmp/atest-store-git.sock" | ||
params: [] | ||
link: "" | ||
enabled: true | ||
categories: [] | ||
description: "" | ||
url: xxx | ||
username: "" | ||
password: "" | ||
readonly: false | ||
disabled: false | ||
properties: {} | ||
- name: ai | ||
kind: | ||
name: atest-ext-ai | ||
dependencies: [] # 无依赖 | ||
url: "unix:///tmp/atest-ext-ai.sock" | ||
params: | ||
- key: "provider" | ||
description: "AI provider (local, openai, claude)" | ||
defaultValue: "local" | ||
- key: "model" | ||
description: "AI model name" | ||
defaultValue: "codellama" | ||
description: "AI provider (ollama, openai, deepseek)" | ||
defaultValue: "ollama" | ||
- key: "endpoint" | ||
description: "AI service endpoint" | ||
description: "AI service endpoint URL" | ||
defaultValue: "http://localhost:11434" | ||
plugins: | ||
- name: atest-store-git | ||
url: unix:///tmp/atest-store-git.sock | ||
enabled: true | ||
- name: atest-ext-ai | ||
url: unix:///tmp/atest-ext-ai.sock | ||
- key: "api_key" | ||
description: "API key for OpenAI/Deepseek providers" | ||
defaultValue: "" | ||
- key: "model" | ||
description: "AI model name (auto-discovered for ollama)" | ||
defaultValue: "" | ||
- key: "max_tokens" | ||
description: "Maximum tokens for AI generation" | ||
defaultValue: "4096" | ||
- key: "temperature" | ||
description: "Generation temperature (0.0-2.0)" | ||
defaultValue: "0.7" | ||
- key: "timeout" | ||
description: "Request timeout duration" | ||
defaultValue: "30s" | ||
link: "https://github.com/LinuxSuRen/atest-ext-ai" | ||
enabled: true | ||
description: "AI Extension Plugin for intelligent SQL generation and execution" | ||
version: "latest" | ||
registry: "ghcr.io/linuxsuren/atest-ext-ai" | ||
categories: ["ai", "sql-generation"] | ||
description: "AI Extension Plugin for natural language to SQL conversion" | ||
url: "unix:///tmp/atest-ext-ai.sock" | ||
username: "" | ||
password: "" | ||
readonly: false | ||
disabled: false | ||
properties: | ||
provider: "ollama" | ||
endpoint: "http://localhost:11434" | ||
api_key: "" | ||
model: "" | ||
max_tokens: "4096" | ||
temperature: "0.7" | ||
timeout: "30s" | ||
|
||
plugins: | ||
- name: atest-store-git | ||
dependencies: [] | ||
url: "unix:///tmp/atest-store-git.sock" | ||
params: [] | ||
link: "" | ||
enabled: true | ||
categories: [] | ||
- name: atest-ext-ai | ||
dependencies: [] | ||
url: "unix:///tmp/atest-ext-ai.sock" | ||
params: | ||
- key: "provider" | ||
description: "AI provider (ollama, openai, deepseek)" | ||
defaultValue: "ollama" | ||
- key: "endpoint" | ||
description: "AI service endpoint" | ||
defaultValue: "http://localhost:11434" | ||
- key: "api_key" | ||
description: "API key for external AI services" | ||
defaultValue: "" | ||
- key: "model" | ||
description: "AI model name (auto-discovered for ollama)" | ||
defaultValue: "" | ||
link: "https://github.com/LinuxSuRen/atest-ext-ai" | ||
enabled: true | ||
categories: ["ai", "sql-generation"] | ||
description: "AI Extension Plugin for natural language to SQL conversion" | ||
version: "v0.1.0" | ||
registry: "ghcr.io/linuxsuren/atest-ext-ai" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,9 +18,11 @@ package remote | |
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
"github.com/linuxsuren/api-testing/pkg/logging" | ||
|
@@ -316,6 +318,12 @@ func (g *gRPCLoader) PProf(name string) []byte { | |
} | ||
|
||
func (g *gRPCLoader) Query(query map[string]string) (result testing.DataResult, err error) { | ||
// Detect AI method calls | ||
if method := query["method"]; strings.HasPrefix(method, "ai.") { | ||
return g.handleAIQuery(query) | ||
} | ||
|
||
// Original standard query logic | ||
var dataResult *server.DataQueryResult | ||
offset, _ := strconv.ParseInt(query["offset"], 10, 64) | ||
limit, _ := strconv.ParseInt(query["limit"], 10, 64) | ||
|
@@ -444,3 +452,69 @@ func (g *gRPCLoader) Close() { | |
g.conn.Close() | ||
} | ||
} | ||
|
||
// handleAIQuery handles AI-specific queries | ||
func (g *gRPCLoader) handleAIQuery(query map[string]string) (testing.DataResult, error) { | ||
method := query["method"] | ||
|
||
var dataQuery *server.DataQuery | ||
switch method { | ||
case "ai.generate": | ||
yuluo-yx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
dataQuery = &server.DataQuery{ | ||
Type: "ai", | ||
Key: "generate", | ||
Sql: g.encodeAIGenerateParams(query), | ||
} | ||
case "ai.capabilities": | ||
dataQuery = &server.DataQuery{ | ||
Type: "ai", | ||
Key: "capabilities", | ||
Sql: "", // No additional parameters needed | ||
} | ||
default: | ||
return testing.DataResult{}, fmt.Errorf("unsupported AI method: %s", method) | ||
} | ||
|
||
// Call existing gRPC Query | ||
dataResult, err := g.client.Query(g.ctx, dataQuery) | ||
if err != nil { | ||
return testing.DataResult{}, err | ||
} | ||
|
||
// Convert response to testing.DataResult format | ||
return g.convertAIResponse(dataResult), nil | ||
} | ||
|
||
// encodeAIGenerateParams filters and encodes AI generation parameters into JSON string. | ||
// This function intentionally creates a new map to exclude the "method" field from the query, | ||
// as "method" is used for routing decisions and should not be forwarded to AI plugins. | ||
// Only the actual AI parameters (model, prompt, config) are encoded and sent via the SQL field. | ||
func (g *gRPCLoader) encodeAIGenerateParams(query map[string]string) string { | ||
// Extract only AI-specific parameters, excluding the routing field "method" | ||
params := map[string]string{ | ||
"model": query["model"], | ||
"prompt": query["prompt"], | ||
"config": query["config"], | ||
} | ||
Comment on lines
+494
to
+498
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think it's necessary? It looks like you just convert the map data into a JSON string. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch! Added comments in 6171a02 to clarify the intent. (Or we can use Go structs instead of maps to provide better type safety?) |
||
data, _ := json.Marshal(params) | ||
return string(data) | ||
} | ||
|
||
// convertAIResponse converts AI response to standard format | ||
func (g *gRPCLoader) convertAIResponse(dataResult *server.DataQueryResult) testing.DataResult { | ||
result := testing.DataResult{ | ||
Pairs: pairToMap(dataResult.Data), | ||
} | ||
|
||
// Map AI-specific response fields | ||
if content := result.Pairs["generated_sql"]; content != "" { | ||
result.Pairs["content"] = content // Follow AI interface standard | ||
} | ||
if result.Pairs["error"] != "" { | ||
result.Pairs["success"] = "false" | ||
} else { | ||
result.Pairs["success"] = "true" | ||
} | ||
|
||
return result | ||
} |
Uh oh!
There was an error while loading. Please reload this page.