feat: add AI endpoint manager plugin with configuration and routing capabilities
Some checks failed
Some checks failed
- Introduced `ai_endpoint_mgr` plugin to manage multiple AI provider endpoints. - Added configuration reference documentation for `config.toml`. - Implemented endpoint loading, active endpoint switching, and model mutation. - Included error handling for missing endpoints and configuration failures. - Developed unit tests covering various scenarios including error paths and concurrency.
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
// 在启动时从主机查询获取的服务 vtable 全局指针。
|
||||
static const dstalk_ai_service_t* g_ai_svc = nullptr;
|
||||
static const dstalk_session_service_t* g_session_svc = nullptr;
|
||||
static const dstalk_ai_endpoint_mgr_t* g_endpoint_mgr = nullptr; // I08: AI endpoint manager(可选)/ optional
|
||||
|
||||
// ---- 常量 / Constants ----
|
||||
|
||||
@@ -287,10 +288,19 @@ static void renderStatusBar(AppContext& ctx) {
|
||||
}
|
||||
|
||||
// 状态文本:模型名 | 消息条数 | 流式状态 / Status text: model name | message count | streaming state
|
||||
// I08: 添加 endpoint manager 信息(如果可用)/ add endpoint manager info (if available)
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "%s | %d messages | %s",
|
||||
gs.model_name.c_str(), msgCount,
|
||||
gs.streaming ? "streaming" : "ready");
|
||||
const char* active_ep = nullptr;
|
||||
if (g_endpoint_mgr) active_ep = g_endpoint_mgr->get_active();
|
||||
if (active_ep && active_ep[0]) {
|
||||
snprintf(buf, sizeof(buf), "%s | %d messages | %s | ep:%s",
|
||||
gs.model_name.c_str(), msgCount,
|
||||
gs.streaming ? "streaming" : "ready", active_ep);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s | %d messages | %s",
|
||||
gs.model_name.c_str(), msgCount,
|
||||
gs.streaming ? "streaming" : "ready");
|
||||
}
|
||||
drawText(r, static_cast<float>(PADDING),
|
||||
barY + (STATUS_H - CHAR_H) / 2.0f, buf, COL_WHITE);
|
||||
}
|
||||
@@ -832,6 +842,9 @@ int main(int argc, char* argv[]) {
|
||||
if (!ai_provider) ai_provider = "ai_openai";
|
||||
g_ai_svc = static_cast<const dstalk_ai_service_t*>(dstalk_service_query(ai_provider, 1));
|
||||
g_session_svc = static_cast<const dstalk_session_service_t*>(dstalk_service_query("session", 1));
|
||||
// I08: 查询 AI endpoint manager(可选服务)/ query AI endpoint manager (optional service)
|
||||
g_endpoint_mgr = static_cast<const dstalk_ai_endpoint_mgr_t*>(
|
||||
dstalk_service_query("ai_endpoint_mgr", 1));
|
||||
if (!g_ai_svc) dstalk_log(3, "AI service not found (check plugins directory)");
|
||||
if (!g_session_svc) dstalk_log(3, "Session service not found");
|
||||
|
||||
@@ -899,15 +912,25 @@ int main(int argc, char* argv[]) {
|
||||
std::string& userMsg =
|
||||
ctx.state.messages[ctx.state.messages.size() - 2].content;
|
||||
int rc = -1;
|
||||
if (g_ai_svc) {
|
||||
// I08: 优先通过 endpoint_mgr 路由,fallback 到 g_ai_svc / prefer endpoint_mgr, fallback to g_ai_svc
|
||||
const bool use_mgr = (g_endpoint_mgr && g_endpoint_mgr->count() > 0);
|
||||
if (use_mgr || g_ai_svc) {
|
||||
int hcount = 0;
|
||||
const dstalk_message_t* history = g_session_svc
|
||||
? g_session_svc->history(&hcount) : nullptr;
|
||||
dstalk_chat_result_t result = g_ai_svc->chat_stream(
|
||||
history, hcount, userMsg.c_str(),
|
||||
streamTokenCallback, &ctx);
|
||||
dstalk_chat_result_t result;
|
||||
if (use_mgr) {
|
||||
result = g_endpoint_mgr->chat_stream(
|
||||
nullptr, history, hcount, userMsg.c_str(),
|
||||
streamTokenCallback, &ctx);
|
||||
} else {
|
||||
result = g_ai_svc->chat_stream(
|
||||
history, hcount, userMsg.c_str(),
|
||||
streamTokenCallback, &ctx);
|
||||
}
|
||||
rc = result.ok ? 0 : -1;
|
||||
g_ai_svc->free_result(&result);
|
||||
if (use_mgr) g_endpoint_mgr->free_result(&result);
|
||||
else g_ai_svc->free_result(&result);
|
||||
}
|
||||
|
||||
// 流式传输完成(或被取消)
|
||||
|
||||
Reference in New Issue
Block a user