feat(server): add fallback model and baseurl in schema (#13375)

fix AI-398

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added support for specifying fallback models for multiple AI
providers, enhancing reliability when primary models are unavailable.
* Providers can now fetch and update their list of available models
dynamically from external APIs.
* Configuration options expanded to allow custom base URLs for certain
providers.

* **Bug Fixes**
* Improved model selection logic to use fallback models if the requested
model is not available online.

* **Chores**
* Updated backend dependencies to include authentication support for
Google services.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2025-08-01 15:22:48 +08:00
committed by GitHub
parent 19790c1b9e
commit 5cbcf6f907
14 changed files with 544 additions and 69 deletions
@@ -46,8 +46,18 @@ export const DEFAULT_DIMENSIONS = 256;
export type OpenAIConfig = {
apiKey: string;
baseUrl?: string;
fallback?: {
text?: string;
structured?: string;
image?: string;
embedding?: string;
};
};
const ModelListSchema = z.object({
data: z.array(z.object({ id: z.string() })),
});
const ImageResponseSchema = z.union([
z.object({
data: z.array(z.object({ b64_json: z.string() })),
@@ -271,6 +281,25 @@ export class OpenAIProvider extends CopilotProvider<OpenAIConfig> {
}
}
override async refreshOnlineModels() {
try {
const baseUrl = this.config.baseUrl || 'https://api.openai.com/v1';
if (baseUrl && !this.onlineModelList.length) {
const { data } = await fetch(`${baseUrl}/models`, {
headers: {
Authorization: `Bearer ${this.config.apiKey}`,
'Content-Type': 'application/json',
},
})
.then(r => r.json())
.then(r => ModelListSchema.parse(r));
this.onlineModelList = data.map(model => model.id);
}
} catch (e) {
this.logger.error('Failed to fetch available models', e);
}
}
override getProviderSpecificTools(
toolName: CopilotChatTools,
model: string
@@ -291,6 +320,7 @@ export class OpenAIProvider extends CopilotProvider<OpenAIConfig> {
const fullCond = {
...cond,
outputType: ModelOutputType.Text,
fallbackModel: this.config.fallback?.text,
};
await this.checkParams({ messages, cond: fullCond, options });
const model = this.selectModel(fullCond);
@@ -331,6 +361,7 @@ export class OpenAIProvider extends CopilotProvider<OpenAIConfig> {
const fullCond = {
...cond,
outputType: ModelOutputType.Text,
fallbackModel: this.config.fallback?.text,
};
await this.checkParams({ messages, cond: fullCond, options });
const model = this.selectModel(fullCond);
@@ -376,7 +407,11 @@ export class OpenAIProvider extends CopilotProvider<OpenAIConfig> {
messages: PromptMessage[],
options: CopilotChatOptions = {}
): AsyncIterable<StreamObject> {
const fullCond = { ...cond, outputType: ModelOutputType.Object };
const fullCond = {
...cond,
outputType: ModelOutputType.Object,
fallbackModel: this.config.fallback?.text,
};
await this.checkParams({ cond: fullCond, messages, options });
const model = this.selectModel(fullCond);
@@ -409,7 +444,11 @@ export class OpenAIProvider extends CopilotProvider<OpenAIConfig> {
messages: PromptMessage[],
options: CopilotStructuredOptions = {}
): Promise<string> {
const fullCond = { ...cond, outputType: ModelOutputType.Structured };
const fullCond = {
...cond,
outputType: ModelOutputType.Structured,
fallbackModel: this.config.fallback?.structured,
};
await this.checkParams({ messages, cond: fullCond, options });
const model = this.selectModel(fullCond);
@@ -449,7 +488,11 @@ export class OpenAIProvider extends CopilotProvider<OpenAIConfig> {
chunkMessages: PromptMessage[][],
options: CopilotChatOptions = {}
): Promise<number[]> {
const fullCond = { ...cond, outputType: ModelOutputType.Text };
const fullCond = {
...cond,
outputType: ModelOutputType.Text,
fallbackModel: this.config.fallback?.text,
};
await this.checkParams({ messages: [], cond: fullCond, options });
const model = this.selectModel(fullCond);
// get the log probability of "yes"/"no"
@@ -594,7 +637,11 @@ export class OpenAIProvider extends CopilotProvider<OpenAIConfig> {
messages: PromptMessage[],
options: CopilotImageOptions = {}
) {
const fullCond = { ...cond, outputType: ModelOutputType.Image };
const fullCond = {
...cond,
outputType: ModelOutputType.Image,
fallbackModel: this.config.fallback?.image,
};
await this.checkParams({ messages, cond: fullCond, options });
const model = this.selectModel(fullCond);
@@ -644,7 +691,11 @@ export class OpenAIProvider extends CopilotProvider<OpenAIConfig> {
options: CopilotEmbeddingOptions = { dimensions: DEFAULT_DIMENSIONS }
): Promise<number[][]> {
messages = Array.isArray(messages) ? messages : [messages];
const fullCond = { ...cond, outputType: ModelOutputType.Embedding };
const fullCond = {
...cond,
outputType: ModelOutputType.Embedding,
fallbackModel: this.config.fallback?.embedding,
};
await this.checkParams({ embeddings: messages, cond: fullCond, options });
const model = this.selectModel(fullCond);