Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 0 additions & 86 deletions console/atest-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 63 additions & 0 deletions console/atest-ui/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,69 @@
"https": "HTTPS Proxy",
"no": "No Proxy"
},
"ai": {
"title": "AI Assistant",
"subtitle": "Natural language to SQL query generator",
"settings": {
"title": "Settings",
"provider": "Provider",
"endpoint": "Endpoint",
"model": "Model",
"apiKey": "API Key",
"temperature": "Temperature",
"maxTokens": "Max Tokens",
"advanced": "Advanced Settings",
"localServices": "Local Services",
"cloudServices": "Cloud Services"
},
"button": {
"generate": "Generate",
"save": "Save",
"reset": "Reset",
"refresh": "Refresh",
"testConnection": "Test Connection",
"configure": "Configure",
"close": "Close"
},
"status": {
"connected": "Connected",
"disconnected": "Disconnected",
"connecting": "Connecting..."
},
"input": {
"placeholder": "Enter your query in natural language..."
},
"option": {
"includeExplanation": "Include explanation"
},
"welcome": {
"title": "Welcome to AI Assistant",
"message": "Configure your AI provider to get started",
"noModels": "No AI models found"
},
"provider": {
"ollama": {
"name": "Ollama",
"description": "Local AI, privacy-first"
},
"openai": {
"name": "OpenAI",
"description": "GPT-4 and more"
},
"deepseek": {
"name": "DeepSeek",
"description": "Powerful reasoning AI"
}
},
"message": {
"configSaved": "Configuration saved successfully",
"configSaveFailed": "Saved locally, backend sync failed",
"connectionSuccess": "Connection successful!",
"connectionFailed": "Connection failed",
"generating": "Generating SQL...",
"copiedSuccess": "Copied to clipboard"
}
},
"//see http spec": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403",
"httpCode": {
"200": "200 OK",
Expand Down
63 changes: 63 additions & 0 deletions console/atest-ui/src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,69 @@
"https": "HTTPS 代理",
"no": "跳过代理"
},
"ai": {
"title": "AI 助手",
"subtitle": "自然语言转SQL查询生成器",
"settings": {
"title": "设置",
"provider": "提供者",
"endpoint": "端点",
"model": "模型",
"apiKey": "API密钥",
"temperature": "温度",
"maxTokens": "最大令牌数",
"advanced": "高级设置",
"localServices": "本地服务",
"cloudServices": "云服务"
},
"button": {
"generate": "生成",
"save": "保存",
"reset": "重置",
"refresh": "刷新",
"testConnection": "测试连接",
"configure": "配置",
"close": "关闭"
},
"status": {
"connected": "已连接",
"disconnected": "已断开",
"connecting": "连接中..."
},
"input": {
"placeholder": "用自然语言输入您的查询..."
},
"option": {
"includeExplanation": "包含解释"
},
"welcome": {
"title": "欢迎使用AI助手",
"message": "配置您的AI提供者以开始使用",
"noModels": "未检测到AI模型"
},
"provider": {
"ollama": {
"name": "Ollama",
"description": "本地AI,隐私优先"
},
"openai": {
"name": "OpenAI",
"description": "GPT-4等模型"
},
"deepseek": {
"name": "DeepSeek",
"description": "强大的推理AI"
}
},
"message": {
"configSaved": "配置保存成功",
"configSaveFailed": "本地保存成功,后端同步失败",
"connectionSuccess": "连接成功!",
"connectionFailed": "连接失败",
"generating": "生成SQL中...",
"copiedSuccess": "已复制到剪贴板"
}
},
"httpCode": {
"200": "200 成功",
"201": "201 已创建",
Expand Down
50 changes: 40 additions & 10 deletions console/atest-ui/src/views/Extension.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
<script setup lang="ts">
import { ref } from 'vue'
import { API } from './net';
import { useI18n } from 'vue-i18n'
import { API } from './net'
import { Cache } from './cache'

interface Props {
name: string
}
const props = defineProps<Props>()
const loading = ref(true)

// Prepare i18n for plugin context
const { t, locale } = useI18n()
const loadPlugin = async (): Promise<void> => {
try {
API.GetPageOfCSS(props.name, (d) => {
Expand All @@ -21,16 +26,41 @@ const loadPlugin = async (): Promise<void> => {
script.textContent = d.message;
document.head.appendChild(script);

const plugin = window.ATestPlugin;

if (plugin && plugin.mount) {
console.log('extension load success');
const container = document.getElementById("plugin-container");
if (container) {
container.innerHTML = ''; // Clear previous content
plugin.mount(container);
// Implement retry mechanism with exponential backoff and context handover
const checkPluginLoad = (retries = 0, maxRetries = 10) => {
const globalScope = globalThis as {
ATestPlugin?: { mount?: (el: Element, context?: unknown) => void }
};
const plugin = globalScope.ATestPlugin;

if (plugin && typeof plugin.mount === 'function') {
console.log('extension load success');
const container = document.getElementById('plugin-container');
if (container) {
container.innerHTML = '';

const context = {
i18n: { t, locale },
API,
Cache
};

try {
plugin.mount(container, context);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已有的插件只有一个参数,这样会导致不兼容。另外 i18n 应该是在插件里实现。不可能把所有插件的 i18n 都写在 core 里。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

收到这个问题!

} catch (error) {
console.error('extension mount error:', error);
}
}
loading.value = false;
} else if (retries < maxRetries) {
const delay = 50 + retries * 50;
setTimeout(() => checkPluginLoad(retries + 1, maxRetries), delay);
} else {
loading.value = false;
}
}
};

checkPluginLoad();
});
} catch (error) {
console.log(`extension load error: ${(error as Error).message}`)
Expand Down
5 changes: 5 additions & 0 deletions pkg/server/remote_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,9 @@ func (s *server) GetStores(ctx context.Context, in *SimpleQuery) (reply *Stores,
wg := sync.WaitGroup{}
mu := sync.Mutex{}
for _, item := range stores {
// Auto-fill socket URL if empty
handleStore(&item)

skip := false
for _, kind := range kinds.Data {
if in != nil && in.Kind != "" && !slices.Contains(kind.Categories, in.Kind) {
Expand Down Expand Up @@ -1670,6 +1673,8 @@ func (s *server) getLoaderByStoreName(storeName string) (loader testing.Writer,
var store *testing.Store
store, err = testing.NewStoreFactory(s.configDir).GetStore(storeName)
if err == nil && store != nil {
// Auto-fill socket URL if empty
handleStore(store)
loader, err = s.storeWriterFactory.NewInstance(*store)
if err != nil {
err = fmt.Errorf("failed to new grpc loader from store %s, err: %v", store.Name, err)
Expand Down
Loading