Merge pull request #1529 from IanShaw027/feat/group-messages-dispatch-redo
feat: 为openai分组增加messages调度模型映射并支持instructions模板注入
This commit is contained in:
@@ -369,6 +369,13 @@ export type GroupPlatform = 'anthropic' | 'openai' | 'gemini' | 'antigravity'
|
||||
|
||||
export type SubscriptionType = 'standard' | 'subscription'
|
||||
|
||||
export interface OpenAIMessagesDispatchModelConfig {
|
||||
opus_mapped_model?: string
|
||||
sonnet_mapped_model?: string
|
||||
haiku_mapped_model?: string
|
||||
exact_model_mappings?: Record<string, string>
|
||||
}
|
||||
|
||||
export interface Group {
|
||||
id: number
|
||||
name: string
|
||||
@@ -391,6 +398,8 @@ export interface Group {
|
||||
fallback_group_id_on_invalid_request: number | null
|
||||
// OpenAI Messages 调度开关(用户侧需要此字段判断是否展示 Claude Code 教程)
|
||||
allow_messages_dispatch?: boolean
|
||||
default_mapped_model?: string
|
||||
messages_dispatch_model_config?: OpenAIMessagesDispatchModelConfig
|
||||
require_oauth_only: boolean
|
||||
require_privacy_set: boolean
|
||||
created_at: string
|
||||
@@ -417,6 +426,7 @@ export interface AdminGroup extends Group {
|
||||
|
||||
// OpenAI Messages 调度配置(仅 openai 平台使用)
|
||||
default_mapped_model?: string
|
||||
messages_dispatch_model_config?: OpenAIMessagesDispatchModelConfig
|
||||
|
||||
// 分组排序
|
||||
sort_order: number
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
createDefaultMessagesDispatchFormState,
|
||||
messagesDispatchConfigToFormState,
|
||||
messagesDispatchFormStateToConfig,
|
||||
resetMessagesDispatchFormState,
|
||||
} from "../groupsMessagesDispatch";
|
||||
|
||||
describe("groupsMessagesDispatch", () => {
|
||||
it("returns the expected default form state", () => {
|
||||
expect(createDefaultMessagesDispatchFormState()).toEqual({
|
||||
allow_messages_dispatch: false,
|
||||
opus_mapped_model: "gpt-5.4",
|
||||
sonnet_mapped_model: "gpt-5.3-codex",
|
||||
haiku_mapped_model: "gpt-5.4-mini",
|
||||
exact_model_mappings: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("sanitizes exact model mapping rows when converting to config", () => {
|
||||
const config = messagesDispatchFormStateToConfig({
|
||||
allow_messages_dispatch: true,
|
||||
opus_mapped_model: " gpt-5.4 ",
|
||||
sonnet_mapped_model: "gpt-5.3-codex",
|
||||
haiku_mapped_model: " gpt-5.4-mini ",
|
||||
exact_model_mappings: [
|
||||
{
|
||||
claude_model: " claude-sonnet-4-5-20250929 ",
|
||||
target_model: " gpt-5.2 ",
|
||||
},
|
||||
{ claude_model: "", target_model: "gpt-5.4" },
|
||||
{ claude_model: "claude-opus-4-6", target_model: " " },
|
||||
],
|
||||
});
|
||||
|
||||
expect(config).toEqual({
|
||||
opus_mapped_model: "gpt-5.4",
|
||||
sonnet_mapped_model: "gpt-5.3-codex",
|
||||
haiku_mapped_model: "gpt-5.4-mini",
|
||||
exact_model_mappings: {
|
||||
"claude-sonnet-4-5-20250929": "gpt-5.2",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("hydrates form state from api config", () => {
|
||||
expect(
|
||||
messagesDispatchConfigToFormState({
|
||||
opus_mapped_model: "gpt-5.4",
|
||||
sonnet_mapped_model: "gpt-5.2",
|
||||
haiku_mapped_model: "gpt-5.4-mini",
|
||||
exact_model_mappings: {
|
||||
"claude-opus-4-6": "gpt-5.4",
|
||||
"claude-haiku-4-5-20251001": "gpt-5.4-mini",
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
allow_messages_dispatch: false,
|
||||
opus_mapped_model: "gpt-5.4",
|
||||
sonnet_mapped_model: "gpt-5.2",
|
||||
haiku_mapped_model: "gpt-5.4-mini",
|
||||
exact_model_mappings: [
|
||||
{
|
||||
claude_model: "claude-haiku-4-5-20251001",
|
||||
target_model: "gpt-5.4-mini",
|
||||
},
|
||||
{ claude_model: "claude-opus-4-6", target_model: "gpt-5.4" },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("resets mutable form state when platform switches away from openai", () => {
|
||||
const state = {
|
||||
allow_messages_dispatch: true,
|
||||
opus_mapped_model: "gpt-5.2",
|
||||
sonnet_mapped_model: "gpt-5.4",
|
||||
haiku_mapped_model: "gpt-5.1",
|
||||
exact_model_mappings: [
|
||||
{ claude_model: "claude-opus-4-6", target_model: "gpt-5.4" },
|
||||
],
|
||||
};
|
||||
|
||||
resetMessagesDispatchFormState(state);
|
||||
|
||||
expect(state).toEqual({
|
||||
allow_messages_dispatch: false,
|
||||
opus_mapped_model: "gpt-5.4",
|
||||
sonnet_mapped_model: "gpt-5.3-codex",
|
||||
haiku_mapped_model: "gpt-5.4-mini",
|
||||
exact_model_mappings: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
72
frontend/src/views/admin/groupsMessagesDispatch.ts
Normal file
72
frontend/src/views/admin/groupsMessagesDispatch.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import type { OpenAIMessagesDispatchModelConfig } from "@/types";
|
||||
|
||||
export interface MessagesDispatchMappingRow {
|
||||
claude_model: string;
|
||||
target_model: string;
|
||||
}
|
||||
|
||||
export interface MessagesDispatchFormState {
|
||||
allow_messages_dispatch: boolean;
|
||||
opus_mapped_model: string;
|
||||
sonnet_mapped_model: string;
|
||||
haiku_mapped_model: string;
|
||||
exact_model_mappings: MessagesDispatchMappingRow[];
|
||||
}
|
||||
|
||||
export function createDefaultMessagesDispatchFormState(): MessagesDispatchFormState {
|
||||
return {
|
||||
allow_messages_dispatch: false,
|
||||
opus_mapped_model: "gpt-5.4",
|
||||
sonnet_mapped_model: "gpt-5.3-codex",
|
||||
haiku_mapped_model: "gpt-5.4-mini",
|
||||
exact_model_mappings: [],
|
||||
};
|
||||
}
|
||||
|
||||
export function messagesDispatchConfigToFormState(
|
||||
config?: OpenAIMessagesDispatchModelConfig | null,
|
||||
): MessagesDispatchFormState {
|
||||
const defaults = createDefaultMessagesDispatchFormState();
|
||||
const exactMappings = Object.entries(config?.exact_model_mappings || {})
|
||||
.sort(([left], [right]) => left.localeCompare(right))
|
||||
.map(([claude_model, target_model]) => ({ claude_model, target_model }));
|
||||
|
||||
return {
|
||||
allow_messages_dispatch: false,
|
||||
opus_mapped_model:
|
||||
config?.opus_mapped_model?.trim() || defaults.opus_mapped_model,
|
||||
sonnet_mapped_model:
|
||||
config?.sonnet_mapped_model?.trim() || defaults.sonnet_mapped_model,
|
||||
haiku_mapped_model:
|
||||
config?.haiku_mapped_model?.trim() || defaults.haiku_mapped_model,
|
||||
exact_model_mappings: exactMappings,
|
||||
};
|
||||
}
|
||||
|
||||
export function messagesDispatchFormStateToConfig(
|
||||
state: MessagesDispatchFormState,
|
||||
): OpenAIMessagesDispatchModelConfig {
|
||||
const exactModelMappings = Object.fromEntries(
|
||||
state.exact_model_mappings
|
||||
.map((row) => [row.claude_model.trim(), row.target_model.trim()] as const)
|
||||
.filter(([claudeModel, targetModel]) => claudeModel && targetModel),
|
||||
);
|
||||
|
||||
return {
|
||||
opus_mapped_model: state.opus_mapped_model.trim(),
|
||||
sonnet_mapped_model: state.sonnet_mapped_model.trim(),
|
||||
haiku_mapped_model: state.haiku_mapped_model.trim(),
|
||||
exact_model_mappings: exactModelMappings,
|
||||
};
|
||||
}
|
||||
|
||||
export function resetMessagesDispatchFormState(
|
||||
target: MessagesDispatchFormState,
|
||||
): void {
|
||||
const defaults = createDefaultMessagesDispatchFormState();
|
||||
target.allow_messages_dispatch = defaults.allow_messages_dispatch;
|
||||
target.opus_mapped_model = defaults.opus_mapped_model;
|
||||
target.sonnet_mapped_model = defaults.sonnet_mapped_model;
|
||||
target.haiku_mapped_model = defaults.haiku_mapped_model;
|
||||
target.exact_model_mappings = [];
|
||||
}
|
||||
Reference in New Issue
Block a user