fix(docs): address documentation site review
This commit is contained in:
@@ -4,10 +4,11 @@
|
||||
|
||||
## 定义 Config 类型
|
||||
|
||||
在插件中导出一个 `Config` 类型和可选的默认值:
|
||||
在插件中导出一个 `Config` 类型和同名的 Schemastery schema;默认值直接写在 schema 中:
|
||||
|
||||
```typescript
|
||||
import type { Context } from 'cordis'
|
||||
import Schema from 'schemastery'
|
||||
|
||||
export const name = 'my-plugin'
|
||||
|
||||
@@ -17,11 +18,11 @@ export interface Config {
|
||||
verbose?: boolean
|
||||
}
|
||||
|
||||
export const Config = {
|
||||
greeting: 'Hello',
|
||||
maxRetries: 3,
|
||||
verbose: false,
|
||||
}
|
||||
export const Config: Schema<Config> = Schema.object({
|
||||
greeting: Schema.string().default('Hello'),
|
||||
maxRetries: Schema.number().default(3),
|
||||
verbose: Schema.boolean().default(false),
|
||||
})
|
||||
|
||||
export function apply(ctx: Context, config: Config) {
|
||||
console.log(config.greeting) // 用户配置或默认值
|
||||
@@ -37,7 +38,7 @@ export function apply(ctx: Context, config: Config) {
|
||||
maxRetries: 5
|
||||
```
|
||||
|
||||
未提供的字段使用导出的 `Config` 对象中的默认值。
|
||||
插件加载时,Cordis 会通过导出的 schema 校验配置,并填充未提供字段的默认值。不要导出普通对象作为 `Config`,因为它不满足 Cordis 要求的 Standard Schema 接口。
|
||||
|
||||
## Schema 校验
|
||||
|
||||
@@ -92,7 +93,7 @@ export interface Config {
|
||||
|
||||
```typescript
|
||||
export function apply(ctx: Context, config: Config) {
|
||||
if (!ctx.llm.hasAdapter(config.model)) {
|
||||
if (!ctx.llm.models().includes(config.model)) {
|
||||
throw new Error(`Model "${config.model}" is not registered by any LLM adapter`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,8 @@ import type { Context } from 'cordis'
|
||||
export const name = 'hello-plugin'
|
||||
|
||||
export function apply(ctx: Context) {
|
||||
// 监听 agent-loop 的 ready 事件
|
||||
ctx.on('ready', () => {
|
||||
console.log('[hello-plugin] 插件已加载!')
|
||||
})
|
||||
// apply 被调用时,插件的必选依赖已就绪
|
||||
console.log('[hello-plugin] 插件已加载!')
|
||||
}
|
||||
```
|
||||
|
||||
@@ -100,17 +98,14 @@ export default {
|
||||
### 类形式
|
||||
|
||||
```typescript
|
||||
import { Service } from 'cordis'
|
||||
import { Service, type Context } from 'cordis'
|
||||
|
||||
export default class MyService extends Service {
|
||||
static inject = ['tools']
|
||||
|
||||
constructor(ctx: Context) {
|
||||
super(ctx, 'myService')
|
||||
}
|
||||
|
||||
start() {
|
||||
// 服务启动逻辑
|
||||
// 构造函数内完成同步初始化
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -133,14 +133,14 @@ defineTool({
|
||||
// ...
|
||||
presentCall(args) {
|
||||
return {
|
||||
intent: 'terminal',
|
||||
title: `bash(${JSON.stringify(args.command).slice(0, 60)})`,
|
||||
card: 'terminal',
|
||||
title: args.command,
|
||||
}
|
||||
},
|
||||
presentResult(args, result) {
|
||||
return {
|
||||
intent: 'terminal',
|
||||
body: result.content.map(b => b.type === 'text' ? b.text : '').join(''),
|
||||
card: 'terminal',
|
||||
output: result.content.map(b => b.type === 'text' ? b.text : '').join(''),
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -156,9 +156,7 @@ defineTool({
|
||||
// 这样就够了:
|
||||
ctx.tools.register(defineTool({ /* ... */ }))
|
||||
|
||||
// 不需要:
|
||||
// const dispose = ctx.tools.register(...)
|
||||
// ctx.on('dispose', dispose)
|
||||
// 不需要额外保存 disposer 或注册清理逻辑
|
||||
```
|
||||
|
||||
## 完整实战示例
|
||||
|
||||
@@ -24,15 +24,15 @@ Cordis 提供多种事件触发模式,适用于不同场景:
|
||||
|
||||
### emit — 广播
|
||||
|
||||
所有监听器并行执行,不关心返回值:
|
||||
所有监听器同步执行,不关心返回值:
|
||||
|
||||
```typescript
|
||||
// 触发
|
||||
ctx.emit('agent/turn-end', { agentId, turnIndex })
|
||||
ctx.emit('my-plugin/ready', { id: 'worker-1' })
|
||||
|
||||
// 监听
|
||||
ctx.on('agent/turn-end', ({ agentId, turnIndex }) => {
|
||||
console.log(`Turn ${turnIndex} ended`)
|
||||
ctx.on('my-plugin/ready', ({ id }) => {
|
||||
console.log(`${id} is ready`)
|
||||
})
|
||||
```
|
||||
|
||||
@@ -53,7 +53,7 @@ ctx.on('some-check', (input) => {
|
||||
|
||||
### serial — 顺序执行
|
||||
|
||||
所有监听器按注册顺序依次执行(异步安全):
|
||||
监听器按注册顺序依次执行,并等待异步结果;第一个返回非空值的监听器会终止后续执行:
|
||||
|
||||
```typescript
|
||||
await ctx.serial('setup-phase', context)
|
||||
@@ -61,18 +61,16 @@ await ctx.serial('setup-phase', context)
|
||||
|
||||
### waterfall — 管道
|
||||
|
||||
每个监听器接收前一个的输出,形成数据管道。**必须调用 `next()` 传递给下游**,不调用即为否决:
|
||||
每个监听器可以包装下游返回值,形成处理链。**必须调用 `next()` 传递给下游**,不调用即为否决:
|
||||
|
||||
```typescript
|
||||
// 触发
|
||||
const finalMessages = await ctx.waterfall('llm/pre-request', messages)
|
||||
const output = await ctx.waterfall('my-plugin/transform', input, async () => input)
|
||||
|
||||
// 监听(必须调用 next)
|
||||
ctx.on('llm/pre-request', async (messages, next) => {
|
||||
// 可以修改 messages
|
||||
messages.push(extraMessage)
|
||||
// 必须调用 next() 传递给下一个监听器
|
||||
return next(messages)
|
||||
ctx.on('my-plugin/transform', async (_input, next) => {
|
||||
const downstream = await next()
|
||||
return downstream.trim()
|
||||
})
|
||||
```
|
||||
|
||||
@@ -89,6 +87,7 @@ declare module 'cordis' {
|
||||
interface Events {
|
||||
'my-plugin/ready': (payload: { id: string }) => void
|
||||
'my-plugin/check': (input: string) => boolean | undefined
|
||||
'my-plugin/transform': (input: string, next: () => Promise<string>) => Promise<string>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,20 +95,11 @@ declare module 'cordis' {
|
||||
// 都有正确的类型推导
|
||||
```
|
||||
|
||||
## 命名约定
|
||||
## Cordis 事件与会话记录
|
||||
|
||||
Harness 事件遵循 `namespace/action` 命名:
|
||||
Harness 的 Cordis 事件遵循 `namespace/action` 命名,例如 `agent/pre-step`、`agent/request`、`agent/step-result`、`tools/result` 和 `session/event`。完整签名与触发模式见[Events 目录](../../../../cordis-catalog/events.md)。
|
||||
|
||||
```
|
||||
agent/pre-step — agent 执行一步之前
|
||||
agent/post-step — agent 执行一步之后
|
||||
tool/call — tool 被调用
|
||||
tool/result — tool 返回结果
|
||||
llm/pre-request — LLM 请求发送前
|
||||
session/event — 会话事件被记录
|
||||
compact/start — 压缩开始
|
||||
compact/end — 压缩结束
|
||||
```
|
||||
`turn/*`、`step/*`、`tool/call`、`tool/result` 和 `compact/*` 是持久化的会话事件类型,不是同名 Cordis 事件。需要观察它们时,监听 `session/event` 并检查 `event.type`。
|
||||
|
||||
## 事件也是效果
|
||||
|
||||
@@ -118,7 +108,7 @@ compact/end — 压缩结束
|
||||
```typescript
|
||||
export function apply(ctx: Context) {
|
||||
// 这个监听器在插件 dispose 时自动清理
|
||||
ctx.on('agent/turn-end', handler)
|
||||
ctx.on('tools/result', handler)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -132,14 +122,10 @@ import type { Context } from 'cordis'
|
||||
export const name = 'tool-logger'
|
||||
|
||||
export function apply(ctx: Context) {
|
||||
ctx.on('tool/call', ({ name, args }) => {
|
||||
console.log(`[tool] ${name}(${JSON.stringify(args)})`)
|
||||
})
|
||||
|
||||
ctx.on('tool/result', ({ name, result }) => {
|
||||
ctx.on('tools/result', (exec, result) => {
|
||||
console.log(`[tool] ${exec.name}(${JSON.stringify(exec.arguments)})`)
|
||||
const text = result.content
|
||||
.filter(b => b.type === 'text')
|
||||
.map(b => b.text)
|
||||
.map(block => block.type === 'text' ? block.text : '')
|
||||
.join('')
|
||||
console.log(`[tool result] ${text.slice(0, 100)}`)
|
||||
})
|
||||
|
||||
@@ -105,14 +105,6 @@ fiber.dispose()
|
||||
export function apply(ctx: Context) {
|
||||
console.log('plugin loading')
|
||||
|
||||
ctx.on('ready', () => {
|
||||
console.log('context ready')
|
||||
})
|
||||
|
||||
ctx.on('dispose', () => {
|
||||
console.log('plugin disposing')
|
||||
})
|
||||
|
||||
ctx.effect(() => {
|
||||
console.log('effect registered')
|
||||
return () => console.log('effect cleaned up')
|
||||
@@ -124,12 +116,10 @@ export function apply(ctx: Context) {
|
||||
```
|
||||
plugin loading
|
||||
effect registered
|
||||
context ready
|
||||
```
|
||||
|
||||
卸载时输出(逆序):
|
||||
卸载时输出:
|
||||
```
|
||||
plugin disposing
|
||||
effect cleaned up
|
||||
```
|
||||
|
||||
|
||||
@@ -90,8 +90,11 @@ export default class MetricsService extends Service {
|
||||
// 必选:服务不存在时,插件不会加载
|
||||
export const inject = ['tools']
|
||||
|
||||
// 可选:服务不存在时,插件仍然加载,但 ctx.xxx 可能是 undefined
|
||||
export const inject = { optional: ['metrics'] }
|
||||
// 可选:不写入 inject,使用时通过 ctx.get() 查询
|
||||
export function apply(ctx: Context) {
|
||||
const metrics = ctx.get('metrics')
|
||||
metrics?.record('plugin_loaded', 1)
|
||||
}
|
||||
```
|
||||
|
||||
### 服务消失时的行为
|
||||
@@ -109,7 +112,10 @@ export const inject = { optional: ['metrics'] }
|
||||
|
||||
```yaml
|
||||
- id: group-a
|
||||
name: 'group:'
|
||||
name: '@cordisjs/plugin-group'
|
||||
group: true
|
||||
isolate:
|
||||
bash: true
|
||||
config:
|
||||
- name: '@deepseek-ai/dsh-bash-local'
|
||||
config:
|
||||
@@ -117,7 +123,10 @@ export const inject = { optional: ['metrics'] }
|
||||
- name: './src/plugin-a.ts'
|
||||
|
||||
- id: group-b
|
||||
name: 'group:'
|
||||
name: '@cordisjs/plugin-group'
|
||||
group: true
|
||||
isolate:
|
||||
bash: true
|
||||
config:
|
||||
- name: '@deepseek-ai/dsh-bash-local'
|
||||
config:
|
||||
|
||||
@@ -114,6 +114,8 @@ interface GenerateOptions {
|
||||
maxTokens?: number
|
||||
/** 温度 */
|
||||
temperature?: number
|
||||
/** 取消或卸载时中止进行中的请求 */
|
||||
signal?: AbortSignal
|
||||
}
|
||||
```
|
||||
|
||||
@@ -160,7 +162,10 @@ mock 适配器是学习 StreamChunk 协议的最佳起点——它用纯本地
|
||||
|
||||
```typescript
|
||||
async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
|
||||
const response = await fetch(this.endpoint, { /* ... */ })
|
||||
const response = await fetch(this.endpoint, {
|
||||
// ...method、headers 和 body
|
||||
signal: options.signal,
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error(`API error: ${response.status}`)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ hero:
|
||||
link: /develop/basic/
|
||||
features:
|
||||
- title: 插件化架构
|
||||
details: 基于 Cordis 效果系统,所有能力通过插件注册,加载即生效、卸载即还原。
|
||||
details: 基于 Cordis 插件系统,所有能力通过插件注册,加载即生效、卸载即还原。
|
||||
- title: 配置即组合
|
||||
details: 一个 cordis.yml 决定整个 Agent 的能力组合——换模型、加工具,只需改一行配置。
|
||||
- title: 开箱即用
|
||||
|
||||
@@ -4,7 +4,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import type { DocsPage } from '../website/docs.ts'
|
||||
import { docsPages, type DocsPage } from '../website/docs.ts'
|
||||
import { addProjectionFrontmatter, rewriteMarkdown } from './project-doc-site.ts'
|
||||
|
||||
const roots: string[] = []
|
||||
@@ -25,8 +25,10 @@ function fixture(): { root: string; pages: DocsPage[] } {
|
||||
return {
|
||||
root,
|
||||
pages: [
|
||||
{ source: 'docs/a.md', route: 'en/a.md', label: 'A', sidebar: 'en-docs', section: 'Test', order: 1 },
|
||||
{ source: 'docs/b.md', route: 'en/reference/b.md', label: 'B', sidebar: 'en-docs', section: 'Test', order: 2 },
|
||||
{ locale: 'root', contentLocale: 'en-US', source: 'docs/a.md', route: 'a.md', label: 'A', sidebar: 'zh-reference', section: 'Test', order: 1 },
|
||||
{ locale: 'root', contentLocale: 'en-US', source: 'docs/b.md', route: 'reference-root/b.md', label: 'B', sidebar: 'zh-reference', section: 'Test', order: 2 },
|
||||
{ locale: 'en', contentLocale: 'en-US', source: 'docs/a.md', route: 'en/a.md', label: 'A', sidebar: 'en-reference', section: 'Test', order: 1 },
|
||||
{ locale: 'en', contentLocale: 'en-US', source: 'docs/b.md', route: 'en/reference/b.md', label: 'B', sidebar: 'en-reference', section: 'Test', order: 2 },
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -36,6 +38,7 @@ describe('rewriteMarkdown', () => {
|
||||
const { root, pages } = fixture()
|
||||
const source = '[B](b.md#part) [source](../packages/tool.ts:2) [web](https://example.com)\n'
|
||||
expect(rewriteMarkdown(source, {
|
||||
locale: 'en',
|
||||
sourcePath: 'docs/a.md',
|
||||
route: 'en/a.md',
|
||||
pages,
|
||||
@@ -48,9 +51,22 @@ describe('rewriteMarkdown', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('selects the published target in the current site locale', () => {
|
||||
const { root, pages } = fixture()
|
||||
expect(rewriteMarkdown('[B](b.md)\n', {
|
||||
locale: 'root',
|
||||
sourcePath: 'docs/a.md',
|
||||
route: 'a.md',
|
||||
pages,
|
||||
repoRoot: root,
|
||||
repositoryRef: 'abc123',
|
||||
})).toBe('[B](./reference-root/b.md)\n')
|
||||
})
|
||||
|
||||
it('uses raw GitHub content for unpublished images', () => {
|
||||
const { root, pages } = fixture()
|
||||
expect(rewriteMarkdown('\n', {
|
||||
locale: 'en',
|
||||
sourcePath: 'docs/a.md',
|
||||
route: 'en/a.md',
|
||||
pages,
|
||||
@@ -63,6 +79,7 @@ describe('rewriteMarkdown', () => {
|
||||
const { root, pages } = fixture()
|
||||
const source = '```md\n[B](b.md)\n```\n'
|
||||
expect(rewriteMarkdown(source, {
|
||||
locale: 'en',
|
||||
sourcePath: 'docs/a.md',
|
||||
route: 'en/a.md',
|
||||
pages,
|
||||
@@ -74,6 +91,7 @@ describe('rewriteMarkdown', () => {
|
||||
it('fails loud when a relative target is missing', () => {
|
||||
const { root, pages } = fixture()
|
||||
expect(() => rewriteMarkdown('[missing](missing.md)\n', {
|
||||
locale: 'en',
|
||||
sourcePath: 'docs/a.md',
|
||||
route: 'en/a.md',
|
||||
pages,
|
||||
@@ -83,6 +101,19 @@ describe('rewriteMarkdown', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('docsPages locale routes', () => {
|
||||
it('publishes the same canonical source at every corresponding locale route', () => {
|
||||
const byRoute = new Map(docsPages.map(page => [page.route, page]))
|
||||
for (const page of docsPages.filter(page => page.locale === 'root')) {
|
||||
const counterpart = byRoute.get(`en/${page.route}`)
|
||||
expect(counterpart, page.route).toBeDefined()
|
||||
expect(counterpart?.locale).toBe('en')
|
||||
expect(counterpart?.source).toBe(page.source)
|
||||
expect(counterpart?.contentLocale).toBe(page.contentLocale)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('addProjectionFrontmatter', () => {
|
||||
it('adds frontmatter to an ordinary Markdown page', () => {
|
||||
expect(addProjectionFrontmatter('# Guide\n', 'docs/guide.md')).toBe(
|
||||
|
||||
@@ -11,7 +11,7 @@ import { fromMarkdown } from 'mdast-util-from-markdown'
|
||||
import { gfmFromMarkdown } from 'mdast-util-gfm'
|
||||
import { gfm } from 'micromark-extension-gfm'
|
||||
import type { Nodes } from 'mdast'
|
||||
import { docsPages, type DocsPage } from '../website/docs.ts'
|
||||
import { docsPages, type DocsLocale, type DocsPage } from '../website/docs.ts'
|
||||
|
||||
const REPOSITORY_URL = 'https://github.com/deepseek-harness/deepseek-harness'
|
||||
const root = resolve(import.meta.dirname, '..')
|
||||
@@ -25,6 +25,7 @@ interface Replacement {
|
||||
|
||||
/** Inputs for rewriting one canonical Markdown page. */
|
||||
export interface RewriteMarkdownOptions {
|
||||
locale: DocsLocale
|
||||
sourcePath: string
|
||||
route: string
|
||||
pages: DocsPage[]
|
||||
@@ -62,14 +63,16 @@ function routeTarget(fromRoute: string, toRoute: string, suffix: string): string
|
||||
return `${target.startsWith('.') ? target : `./${target}`}${suffix}`
|
||||
}
|
||||
|
||||
function sourceMap(pages: DocsPage[]): Map<string, DocsPage> {
|
||||
const map = new Map<string, DocsPage>()
|
||||
function sourceMap(pages: DocsPage[]): Map<string, Map<DocsLocale, DocsPage>> {
|
||||
const map = new Map<string, Map<DocsLocale, DocsPage>>()
|
||||
for (const page of pages) {
|
||||
for (const source of [page.source, ...(page.sourceAliases ?? [])]) {
|
||||
if (map.has(source)) {
|
||||
throw new Error(`project-doc-site: duplicate source or alias ${JSON.stringify(source)}.`)
|
||||
const localized = map.get(source) ?? new Map<DocsLocale, DocsPage>()
|
||||
if (localized.has(page.locale)) {
|
||||
throw new Error(`project-doc-site: duplicate source or alias ${JSON.stringify(source)} for locale ${JSON.stringify(page.locale)}.`)
|
||||
}
|
||||
map.set(source, page)
|
||||
localized.set(page.locale, page)
|
||||
map.set(source, localized)
|
||||
}
|
||||
}
|
||||
return map
|
||||
@@ -132,7 +135,7 @@ export function rewriteMarkdown(source: string, options: RewriteMarkdownOptions)
|
||||
if (path === '') return
|
||||
const { absPath, line } = resolveRepositoryTarget(sourceAbs, path, options.repoRoot)
|
||||
const targetPath = repoPath(absPath, options.repoRoot)
|
||||
const page = published.get(targetPath)
|
||||
const page = published.get(targetPath)?.get(options.locale)
|
||||
const nextUrl = page === undefined
|
||||
? githubTarget(absPath, line, suffix, options.repositoryRef, options.repoRoot, node.type === 'image')
|
||||
: routeTarget(options.route, page.route, suffix)
|
||||
@@ -205,6 +208,7 @@ export function projectDocs(): void {
|
||||
const markdown = readFileSync(sourceAbs, 'utf8')
|
||||
const projected = rewriteMarkdown(markdown, {
|
||||
sourcePath: page.source,
|
||||
locale: page.locale,
|
||||
route: page.route,
|
||||
pages: docsPages,
|
||||
repoRoot: root,
|
||||
|
||||
@@ -13,6 +13,14 @@ const sectionOrder = [
|
||||
'基础',
|
||||
'框架能力',
|
||||
'实战',
|
||||
'概念',
|
||||
'生成参考',
|
||||
'数据结构',
|
||||
'开发手册',
|
||||
'Guide',
|
||||
'Basics',
|
||||
'Framework',
|
||||
'Practice',
|
||||
'Concepts',
|
||||
'Generated reference',
|
||||
'Data structures',
|
||||
@@ -20,7 +28,7 @@ const sectionOrder = [
|
||||
]
|
||||
|
||||
function sidebar(collection: DocsPage['sidebar']): DefaultTheme.SidebarItem[] {
|
||||
const pages = docsPages.filter(page => page.sidebar === collection && page.route !== 'index.md')
|
||||
const pages = docsPages.filter(page => page.sidebar === collection)
|
||||
const sections = new Map<string, DocsPage[]>()
|
||||
for (const page of pages) {
|
||||
const entries = sections.get(page.section) ?? []
|
||||
@@ -51,7 +59,36 @@ function escapeVueInterpolation(html: string): string {
|
||||
}
|
||||
|
||||
const sharedTheme: Pick<DefaultTheme.Config, 'search' | 'socialLinks' | 'editLink'> = {
|
||||
search: { provider: 'local' },
|
||||
search: {
|
||||
provider: 'local',
|
||||
options: {
|
||||
locales: {
|
||||
root: {
|
||||
translations: {
|
||||
button: {
|
||||
buttonText: '搜索文档',
|
||||
buttonAriaLabel: '搜索文档',
|
||||
},
|
||||
modal: {
|
||||
displayDetails: '显示详细列表',
|
||||
resetButtonTitle: '清除搜索',
|
||||
backButtonTitle: '关闭搜索',
|
||||
noResultsText: '未找到相关结果',
|
||||
footer: {
|
||||
selectText: '选择',
|
||||
selectKeyAriaLabel: '回车键',
|
||||
navigateText: '切换',
|
||||
navigateUpKeyAriaLabel: '上方向键',
|
||||
navigateDownKeyAriaLabel: '下方向键',
|
||||
closeText: '关闭',
|
||||
closeKeyAriaLabel: 'Esc 键',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/deepseek-harness/deepseek-harness' },
|
||||
],
|
||||
@@ -81,14 +118,22 @@ export default withMermaid({
|
||||
nav: [
|
||||
{ text: '入门', link: '/guide/', activeMatch: '^/guide/' },
|
||||
{ text: '开发', link: '/develop/basic/', activeMatch: '^/develop/' },
|
||||
{ text: 'Reference', link: '/en/', activeMatch: '^/en/' },
|
||||
{ text: '参考', link: '/reference/', activeMatch: '^/reference/' },
|
||||
],
|
||||
sidebar: {
|
||||
'/guide/': sidebar('zh-guide'),
|
||||
'/develop/': sidebar('zh-develop'),
|
||||
'/reference/': sidebar('zh-reference'),
|
||||
},
|
||||
outline: { label: '本页目录' },
|
||||
docFooter: { prev: '上一篇', next: '下一篇' },
|
||||
darkModeSwitchLabel: '外观',
|
||||
lightModeSwitchTitle: '切换到浅色主题',
|
||||
darkModeSwitchTitle: '切换到深色主题',
|
||||
sidebarMenuLabel: '菜单',
|
||||
returnToTopLabel: '返回顶部',
|
||||
langMenuLabel: '切换语言',
|
||||
skipToContentLabel: '跳至内容',
|
||||
},
|
||||
},
|
||||
en: {
|
||||
@@ -97,12 +142,14 @@ export default withMermaid({
|
||||
link: '/en/',
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{ text: 'Concepts', link: '/en/' },
|
||||
{ text: 'Reference', link: '/en/config-catalog' },
|
||||
{ text: '中文指南', link: '/guide/' },
|
||||
{ text: 'Guide', link: '/en/guide/', activeMatch: '^/en/guide/' },
|
||||
{ text: 'Develop', link: '/en/develop/basic/', activeMatch: '^/en/develop/' },
|
||||
{ text: 'Reference', link: '/en/reference/', activeMatch: '^/en/reference/' },
|
||||
],
|
||||
sidebar: {
|
||||
'/en/': sidebar('en-docs'),
|
||||
'/en/guide/': sidebar('en-guide'),
|
||||
'/en/develop/': sidebar('en-develop'),
|
||||
'/en/reference/': sidebar('en-reference'),
|
||||
},
|
||||
editLink: {
|
||||
pattern: ({ frontmatter }: PageData) => {
|
||||
|
||||
+177
-122
@@ -1,20 +1,38 @@
|
||||
/**
|
||||
* Canonical publication manifest for the documentation website.
|
||||
*
|
||||
* Markdown stays in its owning repository tier. This manifest only maps a
|
||||
* source file to its public route and navigation placement.
|
||||
* Markdown stays in its owning repository tier. This manifest maps each
|
||||
* canonical source into matching route trees for both site locales; when a
|
||||
* translation is absent, both routes intentionally project the available
|
||||
* source instead of copying Markdown.
|
||||
*/
|
||||
|
||||
/** Locale key used by the VitePress site. */
|
||||
export type DocsLocale = 'root' | 'en'
|
||||
|
||||
/** Sidebar collection rendered for one locale and top-level module. */
|
||||
type DocsSidebar =
|
||||
| 'zh-guide'
|
||||
| 'zh-develop'
|
||||
| 'zh-reference'
|
||||
| 'en-guide'
|
||||
| 'en-develop'
|
||||
| 'en-reference'
|
||||
|
||||
/** A page projected into the VitePress source tree. */
|
||||
export interface DocsPage {
|
||||
/** VitePress locale whose route tree owns this projection. */
|
||||
locale: DocsLocale
|
||||
/** Language of the canonical source currently projected at this route. */
|
||||
contentLocale: 'zh-CN' | 'en-US'
|
||||
/** Repository-relative canonical Markdown source. */
|
||||
source: string
|
||||
/** VitePress route, including the `.md` suffix. */
|
||||
route: string
|
||||
/** Navigation label shown in the sidebar. */
|
||||
label: string
|
||||
/** Sidebar collection that owns the page. */
|
||||
sidebar: 'zh-guide' | 'zh-develop' | 'en-docs'
|
||||
/** Sidebar collection that owns the page, or null for a locale home page. */
|
||||
sidebar: DocsSidebar | null
|
||||
/** Section label within the sidebar. */
|
||||
section: string
|
||||
/** Stable order within the section. */
|
||||
@@ -23,191 +41,228 @@ export interface DocsPage {
|
||||
sourceAliases?: string[]
|
||||
}
|
||||
|
||||
const zhGuide: DocsPage[] = [
|
||||
interface MirroredPage {
|
||||
source: string
|
||||
route: string
|
||||
contentLocale: DocsPage['contentLocale']
|
||||
label: Record<DocsLocale, string>
|
||||
sidebar: Record<DocsLocale, DocsSidebar | null>
|
||||
section: Record<DocsLocale, string>
|
||||
order: number
|
||||
sourceAliases?: string[]
|
||||
}
|
||||
|
||||
function mirroredPages(pages: MirroredPage[]): DocsPage[] {
|
||||
return pages.flatMap(page => (['root', 'en'] as const).map(locale => ({
|
||||
locale,
|
||||
contentLocale: page.contentLocale,
|
||||
source: page.source,
|
||||
route: locale === 'root' ? page.route : `en/${page.route}`,
|
||||
label: page.label[locale],
|
||||
sidebar: page.sidebar[locale],
|
||||
section: page.section[locale],
|
||||
order: page.order,
|
||||
...(page.sourceAliases === undefined ? {} : { sourceAliases: page.sourceAliases }),
|
||||
})))
|
||||
}
|
||||
|
||||
const homeAndGuide = mirroredPages([
|
||||
{
|
||||
source: 'docs/user/zh-CN/index.md',
|
||||
route: 'index.md',
|
||||
label: 'DeepSeek Harness',
|
||||
sidebar: 'zh-guide',
|
||||
section: '入门',
|
||||
contentLocale: 'zh-CN',
|
||||
label: { root: 'DeepSeek Harness', en: 'DeepSeek Harness' },
|
||||
sidebar: { root: null, en: null },
|
||||
section: { root: '首页', en: 'Home' },
|
||||
order: 0,
|
||||
},
|
||||
{
|
||||
source: 'docs/user/zh-CN/guide/index.md',
|
||||
route: 'guide/index.md',
|
||||
label: '介绍',
|
||||
sidebar: 'zh-guide',
|
||||
section: '入门',
|
||||
contentLocale: 'zh-CN',
|
||||
label: { root: '介绍', en: 'Introduction' },
|
||||
sidebar: { root: 'zh-guide', en: 'en-guide' },
|
||||
section: { root: '入门', en: 'Guide' },
|
||||
order: 1,
|
||||
sourceAliases: ['docs/user/zh-CN/guide'],
|
||||
},
|
||||
{
|
||||
source: 'docs/user/zh-CN/guide/quickstart.md',
|
||||
route: 'guide/quickstart.md',
|
||||
label: '快速开始',
|
||||
sidebar: 'zh-guide',
|
||||
section: '入门',
|
||||
contentLocale: 'zh-CN',
|
||||
label: { root: '快速开始', en: 'Quick start' },
|
||||
sidebar: { root: 'zh-guide', en: 'en-guide' },
|
||||
section: { root: '入门', en: 'Guide' },
|
||||
order: 2,
|
||||
},
|
||||
{
|
||||
source: 'docs/user/zh-CN/guide/config.md',
|
||||
route: 'guide/config.md',
|
||||
label: '配置文件',
|
||||
sidebar: 'zh-guide',
|
||||
section: '入门',
|
||||
contentLocale: 'zh-CN',
|
||||
label: { root: '配置文件', en: 'Configuration' },
|
||||
sidebar: { root: 'zh-guide', en: 'en-guide' },
|
||||
section: { root: '入门', en: 'Guide' },
|
||||
order: 3,
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const zhDevelop: DocsPage[] = [
|
||||
const develop = mirroredPages([
|
||||
{
|
||||
source: 'docs/user/zh-CN/develop/basic/index.md',
|
||||
route: 'develop/basic/index.md',
|
||||
label: '第一个插件',
|
||||
sidebar: 'zh-develop',
|
||||
section: '基础',
|
||||
contentLocale: 'zh-CN',
|
||||
label: { root: '第一个插件', en: 'First plugin' },
|
||||
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
||||
section: { root: '基础', en: 'Basics' },
|
||||
order: 1,
|
||||
sourceAliases: ['docs/user/zh-CN/develop/basic'],
|
||||
},
|
||||
{
|
||||
source: 'docs/user/zh-CN/develop/basic/tool.md',
|
||||
route: 'develop/basic/tool.md',
|
||||
label: '开发一个 Tool',
|
||||
sidebar: 'zh-develop',
|
||||
section: '基础',
|
||||
contentLocale: 'zh-CN',
|
||||
label: { root: '开发一个 Tool', en: 'Build a tool' },
|
||||
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
||||
section: { root: '基础', en: 'Basics' },
|
||||
order: 2,
|
||||
},
|
||||
{
|
||||
source: 'docs/user/zh-CN/develop/basic/config.md',
|
||||
route: 'develop/basic/config.md',
|
||||
label: '插件配置',
|
||||
sidebar: 'zh-develop',
|
||||
section: '基础',
|
||||
contentLocale: 'zh-CN',
|
||||
label: { root: '插件配置', en: 'Plugin configuration' },
|
||||
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
||||
section: { root: '基础', en: 'Basics' },
|
||||
order: 3,
|
||||
},
|
||||
{
|
||||
source: 'docs/user/zh-CN/develop/framework/index.md',
|
||||
route: 'develop/framework/index.md',
|
||||
label: '插件与生命周期',
|
||||
sidebar: 'zh-develop',
|
||||
section: '框架能力',
|
||||
contentLocale: 'zh-CN',
|
||||
label: { root: '插件与生命周期', en: 'Plugin lifecycle' },
|
||||
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
||||
section: { root: '框架能力', en: 'Framework' },
|
||||
order: 1,
|
||||
sourceAliases: ['docs/user/zh-CN/develop/framework'],
|
||||
},
|
||||
{
|
||||
source: 'docs/user/zh-CN/develop/framework/service.md',
|
||||
route: 'develop/framework/service.md',
|
||||
label: '服务与依赖',
|
||||
sidebar: 'zh-develop',
|
||||
section: '框架能力',
|
||||
contentLocale: 'zh-CN',
|
||||
label: { root: '服务与依赖', en: 'Services and dependencies' },
|
||||
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
||||
section: { root: '框架能力', en: 'Framework' },
|
||||
order: 2,
|
||||
},
|
||||
{
|
||||
source: 'docs/user/zh-CN/develop/framework/events.md',
|
||||
route: 'develop/framework/events.md',
|
||||
label: '事件系统',
|
||||
sidebar: 'zh-develop',
|
||||
section: '框架能力',
|
||||
contentLocale: 'zh-CN',
|
||||
label: { root: '事件系统', en: 'Event system' },
|
||||
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
||||
section: { root: '框架能力', en: 'Framework' },
|
||||
order: 3,
|
||||
},
|
||||
{
|
||||
source: 'docs/user/zh-CN/develop/practice/index.md',
|
||||
route: 'develop/practice/index.md',
|
||||
label: '能力的三层拆分',
|
||||
sidebar: 'zh-develop',
|
||||
section: '实战',
|
||||
contentLocale: 'zh-CN',
|
||||
label: { root: '能力的三层拆分', en: 'Capability layering' },
|
||||
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
||||
section: { root: '实战', en: 'Practice' },
|
||||
order: 1,
|
||||
sourceAliases: ['docs/user/zh-CN/develop/practice'],
|
||||
},
|
||||
{
|
||||
source: 'docs/user/zh-CN/develop/practice/llm-adapter.md',
|
||||
route: 'develop/practice/llm-adapter.md',
|
||||
label: 'LLM 适配器',
|
||||
sidebar: 'zh-develop',
|
||||
section: '实战',
|
||||
contentLocale: 'zh-CN',
|
||||
label: { root: 'LLM 适配器', en: 'LLM adapter' },
|
||||
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
||||
section: { root: '实战', en: 'Practice' },
|
||||
order: 2,
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const enOverview: DocsPage[] = ([
|
||||
['docs/architecture.md', 'en/index.md', 'Architecture'],
|
||||
['docs/cordis-primer.md', 'en/cordis-primer.md', 'Cordis primer'],
|
||||
['docs/capability-seams.md', 'en/capability-seams.md', 'Capability services'],
|
||||
['docs/agent-lifecycle.md', 'en/agent-lifecycle.md', 'Agent lifecycle'],
|
||||
['docs/tool-execution-pipeline.md', 'en/tool-execution-pipeline.md', 'Tool execution'],
|
||||
] as const).map(([source, route, label], order) => ({
|
||||
source,
|
||||
route,
|
||||
label,
|
||||
sidebar: 'en-docs',
|
||||
section: 'Concepts',
|
||||
order,
|
||||
}))
|
||||
|
||||
const enCatalogs: DocsPage[] = ([
|
||||
['docs/config-catalog.md', 'en/config-catalog.md', 'Plugin configuration'],
|
||||
['docs/tool-catalog.md', 'en/tool-catalog.md', 'Tool schemas'],
|
||||
['docs/cordis-catalog/services.md', 'en/cordis-catalog/services.md', 'Services'],
|
||||
['docs/cordis-catalog/events.md', 'en/cordis-catalog/events.md', 'Events'],
|
||||
['docs/persistence-catalog.md', 'en/persistence-catalog.md', 'Persistence events'],
|
||||
] as const).map(([source, route, label], order) => ({
|
||||
source,
|
||||
route,
|
||||
label,
|
||||
sidebar: 'en-docs',
|
||||
section: 'Generated reference',
|
||||
order,
|
||||
}))
|
||||
|
||||
const corePages = [
|
||||
['core.md', 'Core data structures'],
|
||||
['session.md', 'Sessions'],
|
||||
['tools.md', 'Tools'],
|
||||
['llm-streaming.md', 'LLM streaming'],
|
||||
['bash.md', 'Bash execution'],
|
||||
['filesystem.md', 'Filesystem'],
|
||||
['code-runtime.md', 'Code runtime'],
|
||||
['compaction.md', 'Compaction'],
|
||||
['subagent.md', 'Subagents'],
|
||||
['workflow.md', 'Workflows'],
|
||||
['skills.md', 'Skills'],
|
||||
['approval.md', 'Approvals'],
|
||||
['user-interaction.md', 'User interaction'],
|
||||
['sandbox.md', 'Sandboxing'],
|
||||
['web.md', 'Web access'],
|
||||
['persistence.md', 'Session persistence'],
|
||||
] as const
|
||||
|
||||
const enCore: DocsPage[] = corePages.map(([file, label], order) => ({
|
||||
source: `docs/core-data-structures/${file}`,
|
||||
route: `en/core-data-structures/${file}`,
|
||||
label,
|
||||
sidebar: 'en-docs',
|
||||
section: 'Data structures',
|
||||
order,
|
||||
...(file === 'core.md' ? { sourceAliases: ['docs/core-data-structures'] } : {}),
|
||||
}))
|
||||
|
||||
const enCookbook: DocsPage[] = ([
|
||||
['adding-a-package.md', 'Adding a package'],
|
||||
['adding-a-tool.md', 'Adding a tool'],
|
||||
['adding-an-llm-adapter.md', 'Adding an LLM adapter'],
|
||||
['extension-cookbook.md', 'Extension patterns'],
|
||||
] as const).map(([file, label], order) => ({
|
||||
source: `docs/cookbook/${file}`,
|
||||
route: `en/cookbook/${file}`,
|
||||
label,
|
||||
sidebar: 'en-docs',
|
||||
section: 'Cookbook',
|
||||
order,
|
||||
}))
|
||||
const reference = mirroredPages([
|
||||
...([
|
||||
['docs/architecture.md', 'reference/index.md', '架构', 'Architecture'],
|
||||
['docs/cordis-primer.md', 'reference/cordis-primer.md', 'Cordis 入门', 'Cordis primer'],
|
||||
['docs/capability-seams.md', 'reference/capability-seams.md', '能力服务', 'Capability services'],
|
||||
['docs/agent-lifecycle.md', 'reference/agent-lifecycle.md', 'Agent 生命周期', 'Agent lifecycle'],
|
||||
['docs/tool-execution-pipeline.md', 'reference/tool-execution-pipeline.md', 'Tool 执行', 'Tool execution'],
|
||||
] as const).map(([source, route, rootLabel, enLabel], order): MirroredPage => ({
|
||||
source,
|
||||
route,
|
||||
contentLocale: 'en-US',
|
||||
label: { root: rootLabel, en: enLabel },
|
||||
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
||||
section: { root: '概念', en: 'Concepts' },
|
||||
order,
|
||||
})),
|
||||
...([
|
||||
['docs/config-catalog.md', 'reference/config-catalog.md', '插件配置', 'Plugin configuration'],
|
||||
['docs/tool-catalog.md', 'reference/tool-catalog.md', 'Tool Schema', 'Tool schemas'],
|
||||
['docs/cordis-catalog/services.md', 'reference/cordis-catalog/services.md', '服务', 'Services'],
|
||||
['docs/cordis-catalog/events.md', 'reference/cordis-catalog/events.md', '事件', 'Events'],
|
||||
['docs/persistence-catalog.md', 'reference/persistence-catalog.md', '持久化事件', 'Persistence events'],
|
||||
] as const).map(([source, route, rootLabel, enLabel], order): MirroredPage => ({
|
||||
source,
|
||||
route,
|
||||
contentLocale: 'en-US',
|
||||
label: { root: rootLabel, en: enLabel },
|
||||
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
||||
section: { root: '生成参考', en: 'Generated reference' },
|
||||
order,
|
||||
})),
|
||||
...([
|
||||
['core.md', '核心数据结构', 'Core data structures'],
|
||||
['scope.md', '作用域', 'Scopes'],
|
||||
['session.md', '会话', 'Sessions'],
|
||||
['system-prompt.md', '系统提示词', 'System prompts'],
|
||||
['tools.md', '工具', 'Tools'],
|
||||
['llm-streaming.md', 'LLM 流式响应', 'LLM streaming'],
|
||||
['bash.md', 'Bash 执行', 'Bash execution'],
|
||||
['filesystem.md', '文件系统', 'Filesystem'],
|
||||
['code-runtime.md', '代码运行时', 'Code runtime'],
|
||||
['compaction.md', '上下文压缩', 'Compaction'],
|
||||
['subagent.md', '子代理', 'Subagents'],
|
||||
['workflow.md', '工作流', 'Workflows'],
|
||||
['skills.md', '技能', 'Skills'],
|
||||
['approval.md', '审批', 'Approvals'],
|
||||
['user-interaction.md', '用户交互', 'User interaction'],
|
||||
['sandbox.md', '沙箱', 'Sandboxing'],
|
||||
['web.md', 'Web 访问', 'Web access'],
|
||||
['persistence.md', '会话持久化', 'Session persistence'],
|
||||
] as const).map(([file, rootLabel, enLabel], order): MirroredPage => ({
|
||||
source: `docs/core-data-structures/${file}`,
|
||||
route: `reference/core-data-structures/${file}`,
|
||||
contentLocale: 'en-US',
|
||||
label: { root: rootLabel, en: enLabel },
|
||||
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
||||
section: { root: '数据结构', en: 'Data structures' },
|
||||
order,
|
||||
...(file === 'core.md' ? { sourceAliases: ['docs/core-data-structures'] } : {}),
|
||||
})),
|
||||
...([
|
||||
['adding-a-package.md', '新增 Package', 'Adding a package'],
|
||||
['adding-a-tool.md', '新增 Tool', 'Adding a tool'],
|
||||
['adding-an-llm-adapter.md', '新增 LLM Adapter', 'Adding an LLM adapter'],
|
||||
['extension-cookbook.md', '扩展模式', 'Extension patterns'],
|
||||
] as const).map(([file, rootLabel, enLabel], order): MirroredPage => ({
|
||||
source: `docs/cookbook/${file}`,
|
||||
route: `reference/cookbook/${file}`,
|
||||
contentLocale: 'en-US',
|
||||
label: { root: rootLabel, en: enLabel },
|
||||
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
||||
section: { root: '开发手册', en: 'Cookbook' },
|
||||
order,
|
||||
})),
|
||||
])
|
||||
|
||||
/** Every canonical page published by the documentation website. */
|
||||
export const docsPages: DocsPage[] = [
|
||||
...zhGuide,
|
||||
...zhDevelop,
|
||||
...enOverview,
|
||||
...enCatalogs,
|
||||
...enCore,
|
||||
...enCookbook,
|
||||
...homeAndGuide,
|
||||
...develop,
|
||||
...reference,
|
||||
]
|
||||
Reference in New Issue
Block a user