feat: add GitLab Duo and CodeBuddy support, update observability settings

This commit is contained in:
decolua
2026-03-30 11:28:07 +07:00
parent 11e6004fcb
commit abbf8ec86f
21 changed files with 779 additions and 141 deletions
+1 -1
View File
@@ -212,7 +212,7 @@ export const PROVIDER_MODELS = {
{ id: "text-embedding-004", name: "Text Embedding 004 (Legacy)", type: "embedding" },
],
openrouter: [
{ id: "auto", name: "Auto (Best Available)" },
// { id: "openrouter/free", name: "Free Models (Auto)" },
],
glm: [
{ id: "glm-5", name: "GLM 5" },
+10
View File
@@ -316,4 +316,14 @@ export const PROVIDERS = {
baseUrl: "https://aiplatform.googleapis.com",
format: "openai"
},
// GitLab Duo - OpenAI-compatible chat endpoint
gitlab: {
baseUrl: "https://gitlab.com/api/v4/chat/completions",
format: "openai",
},
// CodeBuddy (Tencent) - uses device_code polling auth, no chat completions baseUrl needed
codebuddy: {
baseUrl: "https://copilot.tencent.com/v1/chat/completions",
format: "openai",
},
};
+5
View File
@@ -66,6 +66,11 @@ export class DefaultExecutor extends BaseExecutor {
if (!headers["anthropic-version"]) {
headers["anthropic-version"] = "2023-06-01";
}
} else if (this.provider === "gitlab") {
// GitLab Duo uses Bearer token (PAT with ai_features scope, or OAuth access token)
headers["Authorization"] = `Bearer ${credentials.apiKey || credentials.accessToken}`;
} else if (this.provider === "codebuddy") {
headers["Authorization"] = `Bearer ${credentials.apiKey || credentials.accessToken}`;
} else if (this.provider === "kilocode") {
headers["Authorization"] = `Bearer ${credentials.apiKey || credentials.accessToken}`;
if (credentials.providerSpecificData?.orgId) {
@@ -139,7 +139,7 @@ export function openaiResponsesToOpenAIRequest(model, body, stream, credentials)
function: {
name,
description: String(tool.description || ""),
parameters: tool.parameters,
parameters: normalizeToolParameters(tool.parameters),
strict: tool.strict
}
};
@@ -158,6 +158,15 @@ export function openaiResponsesToOpenAIRequest(model, body, stream, credentials)
return result;
}
/**
* Ensure object schema always has properties field (required by Codex Responses API)
*/
function normalizeToolParameters(params) {
if (!params) return { type: "object", properties: {} };
if (params.type === "object" && !params.properties) return { ...params, properties: {} };
return params;
}
/**
* Convert OpenAI Chat Completions to OpenAI Responses API format
*/
@@ -226,7 +235,7 @@ export function openaiToOpenAIResponsesRequest(model, body, stream, credentials)
result.input.push({
type: "function_call",
call_id: clampCallId(tc.id),
name: tc.function?.name || "",
name: tc.function?.name || "_unknown",
arguments: tc.function?.arguments || "{}"
});
}
@@ -260,7 +269,7 @@ export function openaiToOpenAIResponsesRequest(model, body, stream, credentials)
type: "function",
name: tool.function.name,
description: String(tool.function.description || ""),
parameters: tool.function.parameters,
parameters: normalizeToolParameters(tool.function.parameters),
strict: tool.function.strict
};
}