diff --git a/docs/technical/【后端架构】api-server大Handler瘦身执行计划-2026-05-14.md b/docs/technical/【后端架构】api-server大Handler瘦身执行计划-2026-05-14.md index 6502fcdc..4a32ceac 100644 --- a/docs/technical/【后端架构】api-server大Handler瘦身执行计划-2026-05-14.md +++ b/docs/technical/【后端架构】api-server大Handler瘦身执行计划-2026-05-14.md @@ -194,7 +194,7 @@ cargo test -p api-server app --manifest-path server-rs/Cargo.toml 后续建议继续拆分: -- `match3d`: `draft.rs`、`background_and_cover.rs`、`material_sheet.rs`、`apimart_image.rs`。 +- `match3d`: `draft.rs`、`background_and_cover.rs`、`material_sheet.rs`。 - `puzzle`: `session_form.rs`、`draft_compile.rs`、`image_provider.rs`、`errors.rs`。 - `custom_world`: `publish_gate.rs`、`foundation_job.rs`、`foundation_assets.rs`、`errors.rs`。 - `square_hole`: `config.rs`、`errors.rs`。 diff --git a/docs/technical/【后端架构】api-server能力模块化与生成资产Adapter总纲-2026-05-14.md b/docs/technical/【后端架构】api-server能力模块化与生成资产Adapter总纲-2026-05-14.md index 6ace57c1..17597a32 100644 --- a/docs/technical/【后端架构】api-server能力模块化与生成资产Adapter总纲-2026-05-14.md +++ b/docs/technical/【后端架构】api-server能力模块化与生成资产Adapter总纲-2026-05-14.md @@ -215,7 +215,7 @@ Handler 主要在 `story.rs`、`combat.rs`、`runtime_inventory.rs`: | Square Hole 图片重生成 | OpenAI/VectorEngine GPT image helper | URL 下载或 base64/data URL 解码 | `LegacyAssetPrefix::SquareHoleAssets` | 方洞作品图片槽位相关 kind | profile/work + image slot | 调用方包裹 | 生成成功但入库失败保留 Data URL 回包 | | Custom World 场景/封面 | VectorEngine GPT image 2 / OpenAI helper | URL 下载或 base64 解码 | `LegacyAssetPrefix::CustomWorldScenes` 等 | scene/cover/opening storyboard | `custom_world_profile` 或 profile/landmark/scene slot | `custom_world_ai.rs` 调用方包裹 | entity/scene 生成存在 LLM fallback;资产持久化失败按当前错误口径返回 | | Puzzle 图片 | GPT image 2 generations/edits | 无参考图 JSON 创建;有参考图 multipart 编辑;base64/URL 结果归一 | `LegacyAssetPrefix::PuzzleAssets` | puzzle level/background/generated image,另有 `puzzle_background_music` | puzzle profile/run/level slot | `puzzle.rs` 调用方包裹 | connectivity 可按既有规则跳过部分计费;运行态 fallback 保持原逻辑 | -| Match3D 图片 | APIMart/VectorEngine/OpenAI image helper | 下载、切图、透明化、校准后入库 | `LegacyAssetPrefix::Match3DAssets` | cover/background/item material sheet,音频 kind 另列 | match3d profile/session slot | `match3d.rs` 调用方包裹 | 新草稿不回退 Rodin/GLB;部分连接错误按现有计费跳过规则处理 | +| Match3D 图片 | VectorEngine/OpenAI image helper | 下载、切图、透明化、校准后入库 | `LegacyAssetPrefix::Match3DAssets` | cover/background/item material sheet,音频 kind 另列 | match3d profile/session slot | `match3d.rs` 调用方包裹 | 新草稿不回退 Rodin/GLB;部分连接错误按现有计费跳过规则处理 | | Visual Novel 音频 | VectorEngine Suno/Vidu | 任务提交后按 task publish 下载音频 | 视觉小说/creation audio scope | `visual_novel_music`、`visual_novel_ambient_sound` | `visual_novel_scene` + scene id + `music`/`ambient_sound` | `vector_engine_audio_generation.rs` 调用方包裹 | 上游/下载失败显式错误,不混入图片 Adapter | | 通用音频 | VectorEngine Suno/Vidu | 同上 | creation audio scope | background_music/sound_effect 由调用方目标指定 | creation target entity/slot | 调用方包裹 | 不与 VN 场景语义混用 | | 视频 Opening CG | Ark/火山视频 + storyboard | 先生 storyboard,再图生视频,下载 remote video | Custom World 相关 prefix | `custom_world_opening_cg_storyboard`、`custom_world_opening_cg_video` | `custom_world_profile` + opening cg slots | `execute_billable_asset_operation_with_cost` 固定点数 | 配置缺失/超时显式错误,不应静默降级 | diff --git a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md index 39e9c314..aaf9ff20 100644 --- a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md +++ b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md @@ -190,8 +190,8 @@ npm run check:server-rs-ddd ## 外部服务与资产 -- LLM:`GENARRATIVE_LLM_*`,创意 Agent 另用 `APIMART_BASE_URL` / `APIMART_API_KEY`。 -- 图片生成:VectorEngine `gpt-image-2` 图片 provider 归属 `platform-image`,密钥只在后端环境变量中;`api-server` 内的 `openai_image_generation.rs` 只是兼容调用面和外部失败审计桥接,不再承载 provider 协议实现。实际外部生成运行记录统一落 `tracking_event`,`event_key = external_generation_run`,metadata 记录开始 / 结束时间、耗时、状态、成功标记、失败原因、provider task id 和结果摘要,不再写回过时的 `ai_task`。APIMart 只保留给创意 Agent `gpt-5` Responses 文本 / 多模态链路;DashScope 只按仍在使用的历史能力单独处理,不作为 GPT-image-2 兜底。VectorEngine `/v1/images/generations` 和 `/v1/images/edits` 上游 POST 使用 `libcurl` 发送;`reqwest` 只保留给参考图 URL 下载和响应中图片 URL 下载。`/v1/images/edits` 的 multipart 参考图必须作为 libcurl 文件上传 part 发送,字段名为 `image`,实现上使用 `Form::buffer(file_name, bytes)` 并设置 `Content-Type`;不能只用 `contents(...).filename(...)`,否则上游会把请求转码为缺少图片并返回 `image is required`。`request_send` 阶段的 curl timeout / connect error 按可重试传输错误处理,最多尝试 5 次,并使用指数退避加短抖动;排障时优先看 `attempt`、`max_attempts`、`retry_delay_ms`、`reference_image_bytes_total` 和 `request_params`,不要把 `SendRequest` 当成上游业务错误。 +- LLM:通用 LLM 门面继续使用 `GENARRATIVE_LLM_*`;创意 Agent `gpt-5` Responses / Chat Completions 文本链路已于 2026-06 从 APIMart 迁移到 VectorEngine,使用 `VECTOR_ENGINE_BASE_URL` / `VECTOR_ENGINE_API_KEY` 构造 OpenAI-compatible client,`api-server` 会把未带 `/v1` 的 VectorEngine base URL 规范化到 `/v1` 后请求 `/responses`。`APIMART_BASE_URL` / `APIMART_API_KEY` 只作为历史残留,不再作为创意 Agent gpt-5 客户端来源;后续排障时优先确认 VectorEngine `/v1/models`、`/v1/chat/completions` 和 `/v1/responses` 可用性。 +- 图片生成:VectorEngine `gpt-image-2` 图片 provider 归属 `platform-image`,密钥只在后端环境变量中;`api-server` 内的 `openai_image_generation.rs` 只是兼容调用面和外部失败审计桥接,不再承载 provider 协议实现。实际外部生成运行记录统一落 `tracking_event`,`event_key = external_generation_run`,metadata 记录开始 / 结束时间、耗时、状态、成功标记、失败原因、provider task id 和结果摘要,不再写回过时的 `ai_task`。DashScope 只按仍在使用的历史能力单独处理,不作为 GPT-image-2 兜底。VectorEngine `/v1/images/generations` 和 `/v1/images/edits` 上游 POST 使用 `libcurl` 发送;`reqwest` 只保留给参考图 URL 下载和响应中图片 URL 下载。`/v1/images/edits` 的 multipart 参考图必须作为 libcurl 文件上传 part 发送,字段名为 `image`,实现上使用 `Form::buffer(file_name, bytes)` 并设置 `Content-Type`;不能只用 `contents(...).filename(...)`,否则上游会把请求转码为缺少图片并返回 `image is required`。`request_send` 阶段的 curl timeout / connect error 按可重试传输错误处理,最多尝试 5 次,并使用指数退避加短抖动;排障时优先看 `attempt`、`max_attempts`、`retry_delay_ms`、`reference_image_bytes_total` 和 `request_params`,不要把 `SendRequest` 当成上游业务错误。 - Match3D 物品 sheet:关卡整图完成后走 VectorEngine `/v1/images/edits` multipart `image`,模型为 `gpt-image-2`,`2K 1:1` 输出 `10*10` spritesheet;物品 sheet prompt 固定要求纯绿色绿幕背景,后端上传 OSS 前必须把绿幕扣成透明 PNG,并把透明整图写入 `itemSpritesheetImageSrc/itemSpritesheetImageObjectKey`。后端优先按透明 alpha 连通域从该 sheet 识别真实素材矩形并持久化 20 个物品、每个 5 个形态;识别数量不足时才回退 `10*10` 固定网格。通用系列素材图集的行列索引按每行 2 个物品计算,必须落在 `1..=10`,难度只决定运行态加载 3 / 9 / 15 / 20 种。 - Match3D UI spritesheet 和背景派生图:关卡整图作为参考图并发生成 `1K 1:1` UI spritesheet 与 `1K 9:16` 背景图,模型均为 `gpt-image-2`。UI spritesheet prompt 固定要求纯绿色绿幕背景,后端上传 OSS 前必须把绿幕扣成透明 PNG;背景图必须合成为全画幅不透明 PNG。 - Match3D 1:1 容器 UI:VectorEngine `/v1/images/edits` multipart 参考图。该容器参考图是后端生图协议输入,必须通过 `include_bytes!` 随 `api-server` 编译进二进制,避免 API 单独发布或运行目录缺少 `public/` 时生成失败。 diff --git a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md index a470ec19..c45c23ec 100644 --- a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md +++ b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md @@ -1,4 +1,4 @@ -# 本地开发验证与生产运维 +# 本地开发验证与生产运维 更新时间:`2026-06-09` @@ -321,8 +321,9 @@ OpenTelemetry 现阶段默认开启 OTLP traces / metrics / logs,但本地日 - `GENARRATIVE_SPACETIME_TOKEN` - `GENARRATIVE_DATABASE_BACKUP_*` - `GENARRATIVE_LLM_*` -- `APIMART_*` - `VECTOR_ENGINE_*` +- ~~`APIMART_*`~~(已弃用,LLM 文本调用统一迁移到 VectorEngine) +- `APIMART_*`(历史残留,创意 Agent LLM 已迁移到 VectorEngine) - `HYPER3D_*` - `VOLCENGINE_SPEECH_*` - `DASHSCOPE_*` @@ -332,6 +333,14 @@ OpenTelemetry 现阶段默认开启 OTLP traces / metrics / logs,但本地日 结构化创作 / RPG 的 Responses JSON 链路默认不打开 `web_search`;本地和生产如需联网增强,必须显式配置 `GENARRATIVE_RPG_LLM_WEB_SEARCH_ENABLED=true` 或 `GENARRATIVE_CREATION_AGENT_LLM_WEB_SEARCH_ENABLED=true`。如果上游未开通工具,Responses 可能先吐自然语言再返回 `ToolNotOpen`,这类报错应按工具不可用排查,不要先当成 JSON 解析 bug。 +创意 Agent `gpt-5` 文本链路已从 APIMart 切到 VectorEngine:`api-server` 读取 `VECTOR_ENGINE_BASE_URL` / `VECTOR_ENGINE_API_KEY` 构造 OpenAI-compatible LLM client,并自动补齐 `/v1` 前缀用于 Responses 协议。排查或切换密钥后,可在本地运行: + +```bash +node scripts/test-ve-llm.mjs +``` + +该脚本读取仓库根目录 `.env.secrets.local` 中的 `VECTOR_ENGINE_BASE_URL` 和 `VECTOR_ENGINE_API_KEY`,依次探测 `/v1/models`、`/v1/chat/completions`、`/v1/responses`、`gpt-5` Chat Completions 和基础 JSON 输出能力;脚本只输出 HTTP 状态、耗时、模型和截断摘要,不应打印密钥。若 `.env.secrets.local` 不存在,先补本地 secrets 文件再运行,不要把 secrets 提交进仓库。 + ### 手机验证码短信 手机验证码发送走阿里云普通短信 `SendSms`,验证码由 `module-auth` 在当前 `api-server` 进程内生成、哈希存储和校验,不再调用阿里云托管验证码的 `SendSmsVerifyCode` / `CheckSmsVerifyCode`。因此 `api-server` 重启后,已发送但未校验的验证码会失效。 diff --git a/scripts/test-ve-llm.mjs b/scripts/test-ve-llm.mjs new file mode 100644 index 00000000..764a5e55 --- /dev/null +++ b/scripts/test-ve-llm.mjs @@ -0,0 +1,163 @@ +import { readFileSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const root = resolve(__dirname, '..'); + +function loadEnv(path) { + const content = readFileSync(path, 'utf-8'); + const env = {}; + for (const line of content.split('\n')) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#')) continue; + const eqIndex = trimmed.indexOf('='); + if (eqIndex === -1) continue; + const key = trimmed.slice(0, eqIndex).trim(); + let value = trimmed.slice(eqIndex + 1).trim(); + if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { + value = value.slice(1, -1); + } + env[key] = value; + } + return env; +} + +const env = loadEnv(resolve(root, '.env.secrets.local')); +const BASE = env.VECTOR_ENGINE_BASE_URL?.replace(/\/+$/, '') || 'https://api.vectorengine.cn'; +const KEY = env.VECTOR_ENGINE_API_KEY || ''; + +if (!KEY) { + console.error('未找到 VECTOR_ENGINE_API_KEY'); + process.exit(1); +} + +const TIMEOUT_MS = 60_000; + +async function test(name, method, path, body = null) { + const url = `${BASE}${path}`; + const start = Date.now(); + try { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), TIMEOUT_MS); + const headers = { + 'Authorization': `Bearer ${KEY}`, + 'Content-Type': 'application/json', + }; + const options = { method, headers, signal: controller.signal }; + if (body) options.body = JSON.stringify(body); + + const resp = await fetch(url, options); + clearTimeout(timer); + const elapsed = Date.now() - start; + const text = await resp.text(); + let json = null; + try { json = JSON.parse(text); } catch {} + + if (resp.ok) { + const model = json?.model || json?.data?.[0]?.id || '?'; + const summary = json?.choices?.[0] ? `choices[0]: ${json.choices[0].message?.content?.slice(0, 80)}` : + json?.output_text ? `output_text: ${json.output_text.slice(0, 80)}` : + json?.data ? `${json.data.length} models` : JSON.stringify(json).slice(0, 120); + return { ok: true, elapsed, code: resp.status, model, summary }; + } else { + const errMsg = json?.error?.message || json?.message || text.slice(0, 200); + return { ok: false, elapsed, code: resp.status, error: errMsg }; + } + } catch (e) { + const elapsed = Date.now() - start; + return { ok: false, elapsed, code: 0, error: e.name === 'AbortError' ? `超时(${TIMEOUT_MS / 1000}s)` : e.message }; + } +} + +console.log(`VectorEngine LLM 能力探测`); +console.log(`目标: ${BASE}\n`); + +const tests = [ + // 1. 探测 /v1/models - 基础连通性 + 列出可用模型 + { name: 'GET /v1/models (列出可用模型)', method: 'GET', path: '/v1/models' }, + + // 2. Chat Completions - 最标准协议,项目已有 LlmProvider::OpenAiCompatible 支持 + { + name: 'POST /v1/chat/completions (Chat)', + method: 'POST', + path: '/v1/chat/completions', + body: { + model: 'gpt-4o', + messages: [{ role: 'user', content: '回复 ok,不要解释' }], + max_tokens: 10, + }, + }, + + // 3. Responses - Apimart 当前使用的协议 + { + name: 'POST /v1/responses (Responses)', + method: 'POST', + path: '/v1/responses', + body: { + model: 'gpt-4o', + input: [ + { role: 'user', content: [{ type: 'input_text', text: '回复 ok,不要解释' }] }, + ], + }, + }, + + // 4. 测试 gpt-5 (creative_agent 模型) + { + name: 'POST /v1/chat/completions (gpt-5, Chat)', + method: 'POST', + path: '/v1/chat/completions', + body: { + model: 'gpt-5', + messages: [{ role: 'user', content: '回复 ok' }], + max_tokens: 10, + }, + }, + + // 5. 抓大鹅生成需要的 JSON 输出能力验证 + { + name: 'POST /v1/chat/completions (JSON 输出: 抓大鹅物品)', + method: 'POST', + path: '/v1/chat/completions', + body: { + model: 'gpt-4o', + messages: [ + { role: 'system', content: '你是抓大鹅游戏编辑,只返回 JSON。' }, + { role: 'user', content: '题材:水果。请生成 JSON:{"gameName":"水果切切乐","items":[{"name":"苹果","itemSize":"中"},{"name":"西瓜","itemSize":"大"}]}' }, + ], + max_tokens: 200, + }, + }, +]; + +let pass = 0; +let fail = 0; + +for (let i = 0; i < tests.length; i++) { + const t = tests[i]; + console.log(`[${i + 1}/${tests.length}] ${t.name}`); + const result = await test(t.name, t.method, t.path, t.body); + + if (result.ok) { + console.log(` ✅ HTTP ${result.code} ${result.elapsed}ms model: ${result.model}`); + console.log(` ${result.summary}`); + pass++; + } else { + const codeStr = result.code === 0 ? 'NET' : `HTTP ${result.code}`; + console.log(` ❌ ${codeStr} ${result.elapsed}ms ${result.error}`); + fail++; + } + console.log(); +} + +console.log(`=== 结果: ${pass}/${tests.length} 通过, ${fail}/${tests.length} 失败 ===`); + +// 结论 +if (pass >= 3) { + console.log('\n✅ VectorEngine 支持 LLM 文本调用,可替代 Apimart。'); + console.log(' 将 .env.secrets.local 中 APIMART_BASE_URL 改为 VectorEngine 地址即可。'); +} else if (pass <= 1) { + console.log('\n❌ VectorEngine 不支持 LLM 文本调用。'); +} else { + console.log('\n⚠️ 部分支持,需进一步评估。'); +} diff --git a/server-rs/crates/api-server/src/config.rs b/server-rs/crates/api-server/src/config.rs index e9f6ec68..46a5f9f0 100644 --- a/server-rs/crates/api-server/src/config.rs +++ b/server-rs/crates/api-server/src/config.rs @@ -133,9 +133,10 @@ pub struct AppConfig { pub dashscope_reference_image_model: String, pub dashscope_cover_image_model: String, pub dashscope_image_request_timeout_ms: u64, - pub apimart_base_url: String, - pub apimart_api_key: Option, - pub apimart_image_request_timeout_ms: u64, + // 中文注释:Apimart 已于 2026-06 弃用,LLM 文本调用统一迁移到 VectorEngine(同时支持 Chat Completions / Responses 协议)。 + // pub apimart_base_url: String, + // pub apimart_api_key: Option, + // pub apimart_image_request_timeout_ms: u64, pub vector_engine_base_url: String, pub vector_engine_api_key: Option, pub vector_engine_image_request_timeout_ms: u64, @@ -293,9 +294,9 @@ impl Default for AppConfig { dashscope_reference_image_model: String::new(), dashscope_cover_image_model: String::new(), dashscope_image_request_timeout_ms: 150_000, - apimart_base_url: String::new(), - apimart_api_key: None, - apimart_image_request_timeout_ms: 180_000, + // apimart_base_url: String::new(), + // apimart_api_key: None, + // apimart_image_request_timeout_ms: 180_000, vector_engine_base_url: String::new(), vector_engine_api_key: None, vector_engine_image_request_timeout_ms: DEFAULT_VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS, @@ -791,17 +792,17 @@ impl AppConfig { config.dashscope_image_request_timeout_ms = dashscope_image_request_timeout_ms; } - if let Some(apimart_base_url) = read_first_non_empty_env(&["APIMART_BASE_URL"]) { - config.apimart_base_url = apimart_base_url; - } - - config.apimart_api_key = read_first_non_empty_env(&["APIMART_API_KEY"]); - - if let Some(apimart_image_request_timeout_ms) = - read_first_positive_u64_env(&["APIMART_IMAGE_REQUEST_TIMEOUT_MS"]) - { - config.apimart_image_request_timeout_ms = apimart_image_request_timeout_ms; - } + // 中文注释:Apimart 已于 2026-06 弃用,LLM 文本调用统一迁移到 VectorEngine。 + // 保留以下历史加载代码,后续删除: + // if let Some(apimart_base_url) = read_first_non_empty_env(&["APIMART_BASE_URL"]) { + // config.apimart_base_url = apimart_base_url; + // } + // config.apimart_api_key = read_first_non_empty_env(&["APIMART_API_KEY"]); + // if let Some(apimart_image_request_timeout_ms) = + // read_first_positive_u64_env(&["APIMART_IMAGE_REQUEST_TIMEOUT_MS"]) + // { + // config.apimart_image_request_timeout_ms = apimart_image_request_timeout_ms; + // } if let Some(vector_engine_base_url) = read_first_non_empty_env(&["VECTOR_ENGINE_BASE_URL"]) { @@ -1189,7 +1190,7 @@ mod tests { assert!(config.llm_model.is_empty()); assert!(config.llm_base_url.is_empty()); - assert!(config.apimart_base_url.is_empty()); + // assert!(config.apimart_base_url.is_empty()); assert!(config.vector_engine_base_url.is_empty()); assert!(config.ark_character_video_base_url.is_empty()); assert_eq!(config.hyper3d_base_url, "https://api.hyper3d.com/api/v2"); @@ -1285,11 +1286,11 @@ mod tests { assert_eq!(config.llm_provider, LlmProvider::OpenAiCompatible); assert_eq!(config.llm_base_url, "https://llm.internal.example/v1"); assert_eq!(config.llm_model, "internal-text-model"); - assert_eq!( - config.apimart_base_url, - "https://responses.internal.example/v1" - ); - assert_eq!(config.apimart_image_request_timeout_ms, 190_000); + // assert_eq!( + // config.apimart_base_url, + // "https://responses.internal.example/v1" + // ); + // assert_eq!(config.apimart_image_request_timeout_ms, 190_000); assert_eq!( config.vector_engine_base_url, "https://vector.internal.example" diff --git a/server-rs/crates/api-server/src/state.rs b/server-rs/crates/api-server/src/state.rs index 68aa3805..49d0e381 100644 --- a/server-rs/crates/api-server/src/state.rs +++ b/server-rs/crates/api-server/src/state.rs @@ -1378,8 +1378,9 @@ fn build_llm_client(config: &AppConfig) -> Result, AppStateIni fn build_creative_agent_gpt5_client( config: &AppConfig, ) -> Result, AppStateInitError> { + // 中文注释:Apimart 已于 2026-06 弃用,LLM 文本调用统一迁移到 VectorEngine。 let Some(api_key) = config - .apimart_api_key + .vector_engine_api_key .as_ref() .map(|value| value.trim()) .filter(|value| !value.is_empty()) @@ -1387,9 +1388,15 @@ fn build_creative_agent_gpt5_client( return Ok(None); }; + let base_url = if config.vector_engine_base_url.ends_with("/v1") { + config.vector_engine_base_url.clone() + } else { + format!("{}/v1", config.vector_engine_base_url.trim_end_matches('/')) + }; + let llm_config = LlmConfig::new( LlmProvider::OpenAiCompatible, - config.apimart_base_url.clone(), + base_url, api_key.to_string(), platform_agent::CREATIVE_AGENT_GPT5_MODEL.to_string(), config.llm_request_timeout_ms, @@ -1512,11 +1519,11 @@ mod tests { } #[test] - fn app_state_builds_creative_agent_gpt5_client_from_apimart_settings() { + fn app_state_builds_creative_agent_gpt5_client_from_vector_engine_settings() { let mut config = AppConfig::default(); config.llm_api_key = None; - config.apimart_base_url = "https://api.apimart.test/v1".to_string(); - config.apimart_api_key = Some("apimart-key".to_string()); + config.vector_engine_base_url = "https://api.vectorengine.test".to_string(); + config.vector_engine_api_key = Some("ve-key".to_string()); let state = AppState::new(config).expect("state should build"); let client = state @@ -1529,7 +1536,7 @@ mod tests { ); assert_eq!( client.config().responses_url(), - "https://api.apimart.test/v1/responses" + "https://api.vectorengine.test/v1/responses" ); assert!(client.config().official_fallback()); }