chore(merge): 整合最新 main 与统一认证变更
ci / verify (pull_request) Successful in 12m18s

合并远端生产 CI、依赖与 API 文档改动,保留统一认证和 SSF 能力。由于远端生产基线已占用 0062,将尚未发布的身份迁移整理为 0063 至 0068 的最终增量 Schema,移除仅用于开发期草稿升级的破坏性迁移步骤。

验证:go test ./...。其余生产门禁在合并提交后继续执行。
This commit is contained in:
2026-07-17 19:06:37 +08:00
100 changed files with 9673 additions and 1799 deletions
+107
View File
@@ -0,0 +1,107 @@
import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it, vi } from 'vitest';
import type { ApiDocSection, TaskForm } from '../types';
import { ApiDocsPage } from './ApiDocsPage';
describe('ApiDocsPage extended task documentation', () => {
it.each([
['guideBaseUrl', '前往创建 API Key'],
['guideWebhook', 'TASK_PROGRESS_CALLBACK_ENABLED'],
['guideErrors', '400 invalid_parameter'],
['guideTesting', 'simulated=true'],
] as const)('renders the clickable guide page %s', (section, expectedContent) => {
const html = renderDocs(section, { kind: 'chat.completions', model: 'gpt-4o-mini', prompt: '你好' });
expect(html).toContain('data-active="true"');
expect(html).toContain(expectedContent);
expect(html).toContain('指南导航');
expect(html).not.toContain('/v1/chat/completions');
});
it('documents the OpenAI-compatible Responses request and continuity fields', () => {
const html = renderDocs('responses', { kind: 'responses', model: 'gpt-4o-mini', prompt: '你好' });
expect(html).toContain('/v1/responses');
expect(html).toContain('previous_response_id');
expect(html).toContain('X-Async');
expect(html).toContain('连续对话与状态归属');
});
it('documents video generation parameters and async retrieval guidance', () => {
const html = renderDocs('videoGeneration', { kind: 'videos.generations', model: '豆包Seedance-2.0', prompt: '雪山日出' });
expect(html).toContain('/api/v1/videos/generations');
expect(html).toContain('aspect_ratio');
expect(html).toContain('reference_image');
expect(html).toContain('任务取回接口');
});
it('documents async mode as a body-independent capability', () => {
const html = renderDocs('asyncMode', { kind: 'chat.completions', model: 'gpt-4o-mini', prompt: '你好' });
expect(html).toContain('X-Async');
expect(html).toContain('原请求 Header');
expect(html).toContain('同步与异步的区别');
expect(html).toContain('异步受理响应');
expect(html).not.toContain('/api/v1/videos/generations');
expect(html).not.toContain('Body 参数');
expect(html).not.toContain('视频模型 ID');
});
it.each(['chat', 'responses', 'embeddings', 'reranks', 'imageGeneration', 'imageEdit', 'videoGeneration'] as const)(
'shows the shared X-Async switch on the %s task API',
(section) => {
const html = renderDocs(section, { kind: 'chat.completions', model: 'gpt-4o-mini', prompt: '你好' });
expect(html).toContain('X-Async');
},
);
it('renders nested objects and object arrays recursively', () => {
const html = renderDocs('videoGeneration', { kind: 'videos.generations', model: '豆包Seedance-2.0', prompt: '雪山日出' });
expect(html).toContain('array<object>');
expect(html).toContain('data-depth="1"');
expect(html).toContain('data-depth="4"');
expect(html).toContain('inline_element');
expect(html).toContain('refer_images');
expect(html).toContain('slot_key');
});
it('renders task retrieval as GET with a path parameter instead of a request body', () => {
const html = renderDocs('taskRetrieve', { kind: 'tasks.retrieve', model: 'task', prompt: '', taskId: 'task-123' });
expect(html).toContain('GET');
expect(html).toContain('/api/v1/tasks/task-123');
expect(html).toContain('Path 参数');
expect(html).toContain('响应 Body');
expect(html).toContain('result');
expect(html).toContain('billingSummary');
expect(html).toContain('attempts');
expect(html).toContain('成功响应示例');
expect(html).toContain('Task ID');
expect(html).not.toContain('请求 Body');
});
});
function renderDocs(activeDocSection: ApiDocSection, taskForm: TaskForm) {
return renderToStaticMarkup(
<ApiDocsPage
activeDocSection={activeDocSection}
apiKeySecretsById={{}}
apiKeys={[]}
canRun
coreMessage=""
coreState="idle"
selectedApiKeyId=""
taskForm={taskForm}
taskResult={null}
onApiKeyChange={vi.fn()}
onCreateApiKey={vi.fn()}
onDocSectionChange={vi.fn()}
onLogin={vi.fn()}
onSubmitTask={vi.fn()}
onTaskFormChange={vi.fn()}
/>,
);
}
File diff suppressed because it is too large Load Diff