Files
chenxin 3fbcd0437c 🎉 feat: 建立阶段 0 工程骨架与 OpenAPI 契约机器化(WP-01 起步)
- Makefile:统一命令入口(backend/engine/docker/openapi/generate,含 make help)
- backend:FastAPI + uv 最小工程,统一错误体(契约第 7 节)、ADR-015 平台 ID 生成与校验(uuid-utils);pytest 7 项、ruff、mypy strict、启动冒烟全部通过
- engine:灵台引擎 C++23/CMake 骨架,按 Lingtai C ABI 契约实现 lingtai.h(13 个 HjStatus、版本协商、句柄表防二次释放、线程局部错误上下文);doctest 5 用例通过,nm 验证仅导出 hj_* 符号
- openapi:OpenAPI 3.1 单一真源初始版(错误体/分页/Money/首批平台 ID/ContentBlock 全 21 类型/流事件 envelope),Redocly lint 0 错误、bundle 通过
- frontend:Next.js 最小骨架,openapi-typescript 生成 API 类型并通过 strict tsc
- docker:hunjing-postgres(pgvector:17)compose;.env.example 与 config 示例
- ADR-025:登记测试与契约工具链(doctest/libFuzzer/Redocly/openapi-typescript/uuid-utils)
- desktop:Tauri v2 占位与初始化说明(WP-10 启动)
2026-09-09 21:45:04 +08:00

402 lines
15 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
openapi: 3.1.0
info:
title: Hunjing API
version: 0.1.0
summary: Hunjing 平台公共契约(OpenAPI 3.1 单一真源)
license:
name: Apache-2.0
identifier: Apache-2.0
description: |
本文件由 docs/contracts/ 机器化而来,是公共 HTTP 契约的单一真源;
TypeScript/Rust 客户端由本文件生成,不手写重复 DTO。
当前版本覆盖阶段 0 所需基础:错误体、分页、Money、平台 ID、
统一 ContentBlock(21 类型)与流事件 envelope;资源端点随工作包增量登记。
jsonSchemaDialect: https://json-schema.org/draft/2020-12/schema
servers:
- url: https://api.example.dev/v1
description: Native API(开发占位,正式域名见 docs/contracts/api-conventions.md)
paths:
/healthz:
get:
operationId: getHealth
tags: [ops]
summary: 服务健康探针
security: []
responses:
"200":
description: 服务正常
content:
application/json:
schema:
type: object
required: [status]
properties:
status: { type: string, enum: [ok] }
env: { type: string }
default:
description: 错误
content:
application/json:
schema: { $ref: "#/components/schemas/ErrorEnvelope" }
components:
schemas:
# ===== 通用:错误体(api-conventions 第 7 节) =====
Error:
type: object
required: [code, message]
properties:
code:
type: string
description: 稳定机器错误码
examples: [validation_failed]
message: { type: string, description: 可本地化消息 }
trace_id: { type: string, format: uuid }
request_id: { type: string }
details:
type: object
additionalProperties: true
description: 不稳定细节,必须脱敏
ErrorEnvelope:
type: object
required: [error]
properties:
error: { $ref: "#/components/schemas/Error" }
# ===== 通用:分页(opaque cursor) =====
CursorPage:
type: object
required: [data, next_cursor, has_more]
properties:
data: { type: array, items: {} }
next_cursor:
oneOf: [{ type: string }, { type: "null" }]
description: opaque cursor;客户端不得解析
has_more: { type: boolean }
# ===== 通用:金额(最小货币单位整数,禁止浮点) =====
Money:
type: object
required: [amount_minor, currency]
properties:
amount_minor: { type: integer, minimum: 0 }
currency: { type: string, pattern: "^[A-Z]{3}$" }
# ===== 平台 ID(ADR-015:<prefix><lowercase-canonical-uuid>;首批按垂直切片登记) =====
UserId: { type: string, pattern: "^user_[0-9a-f]{12}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }
PersonalDomainId: { type: string, pattern: "^domain_[0-9a-f]{12}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }
ConversationId: { type: string, pattern: "^conversation_[0-9a-f]{12}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }
BranchId: { type: string, pattern: "^branch_[0-9a-f]{12}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }
MessageId: { type: string, pattern: "^message_[0-9a-f]{12}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }
ContentBlockId: { type: string, pattern: "^block_[0-9a-f]{12}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }
RunId: { type: string, pattern: "^run_[0-9a-f]{12}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }
TurnId: { type: string, pattern: "^turn_[0-9a-f]{12}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }
RequestId: { type: string, pattern: "^request_[0-9a-f]{12}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", description: 服务端生成,UUIDv4 }
# ===== 统一 ContentBlock(content-blocks-and-streaming 第 2-3 节) =====
BlockStatus:
type: string
enum: [pending, streaming, completed, failed, cancelled]
BlockAnnotation:
type: object
description: 类型化标注(citation/来源/敏感度/语言)
additionalProperties: true
BlockBase:
type: object
required: [id]
properties:
id: { $ref: "#/components/schemas/ContentBlockId" }
status: { $ref: "#/components/schemas/BlockStatus" }
annotations:
type: array
items: { $ref: "#/components/schemas/BlockAnnotation" }
metadata:
type: object
description: 只允许 Schema 登记字段,不存任意秘密
additionalProperties: true
TextBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [text]
properties:
type: { type: string, enum: [text] }
text: { type: string }
language: { type: string }
MarkdownBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [text]
properties:
type: { type: string, enum: [markdown] }
text: { type: string }
flavor: { type: string, enum: [hunjing-gfm-v1] }
CodeBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [source]
properties:
type: { type: string, enum: [code] }
source: { type: string }
language: { type: string }
filename: { type: string }
QuoteBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [content]
properties:
type: { type: string, enum: [quote] }
content:
type: array
items: { $ref: "#/components/schemas/ContentBlock" }
source_ref: { type: string }
TableBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [columns, rows]
properties:
type: { type: string, enum: [table] }
columns: { type: array, items: { type: string } }
rows: { type: array, items: { type: array, items: {} } }
caption: { type: string }
CalloutBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [kind, content]
properties:
type: { type: string, enum: [callout] }
kind: { type: string, enum: [info, warning, danger, success] }
title: { type: string }
content:
type: array
items: { $ref: "#/components/schemas/ContentBlock" }
MathBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [notation, display, source]
properties:
type: { type: string, enum: [math] }
notation: { type: string, enum: [latex, asciimath] }
display: { type: string, enum: [inline, block] }
source: { type: string }
fallback_text: { type: string, description: 可访问性与解析失败降级描述 }
label: { type: string }
MermaidBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [source]
properties:
type: { type: string, enum: [mermaid] }
source: { type: string }
title: { type: string }
caption: { type: string }
fallback_code_language: { type: string, default: mermaid }
ImageBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [alt]
properties:
type: { type: string, enum: [image] }
file_id: { type: string }
url_ref: { type: string, description: "受控 URL;禁止 file://、javascript: 等协议" }
alt: { type: string }
width: { type: integer, minimum: 1 }
height: { type: integer, minimum: 1 }
caption: { type: string }
VideoBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [file_id]
properties:
type: { type: string, enum: [video] }
file_id: { type: string }
poster_file_id: { type: string }
caption: { type: string }
transcript_file_id: { type: string }
duration_ms: { type: integer, minimum: 0 }
AudioBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [file_id]
properties:
type: { type: string, enum: [audio] }
file_id: { type: string }
transcript: { type: string }
duration_ms: { type: integer, minimum: 0 }
waveform: { type: string }
FileBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [file_id, filename, media_type, size_bytes]
properties:
type: { type: string, enum: [file] }
file_id: { type: string }
filename: { type: string }
media_type: { type: string }
size_bytes: { type: integer, minimum: 0 }
description: { type: string }
GalleryBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [items]
properties:
type: { type: string, enum: [gallery] }
items:
type: array
items:
oneOf:
- $ref: "#/components/schemas/ImageBlock"
- $ref: "#/components/schemas/VideoBlock"
layout: { type: string }
AvatarBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [asset_id, format]
properties:
type: { type: string, enum: [avatar] }
asset_id: { type: string }
format: { type: string, enum: [live2d, vrm, gltf] }
preview_file_id: { type: string }
ToolCallBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [tool_name, call_id]
properties:
type: { type: string, enum: [tool_call] }
tool_name: { type: string }
call_id: { type: string }
arguments_redacted: { type: object, description: 参数脱敏视图 }
approval_id: { type: string }
ToolResultBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [call_id]
properties:
type: { type: string, enum: [tool_result] }
call_id: { type: string }
summary: { type: object, additionalProperties: true }
file_ids: { type: array, items: { type: string } }
is_error: { type: boolean }
AgentStatusBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [stage]
properties:
type: { type: string, enum: [agent_status] }
agent_id: { type: string }
child_run_id: { type: string }
stage: { type: string }
progress: { type: object, additionalProperties: true }
waiting_reason: { type: string }
ApprovalRequestBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [approval_id, action]
properties:
type: { type: string, enum: [approval_request] }
approval_id: { type: string }
action: { type: string }
impact: { type: string }
arguments_summary: { type: object, additionalProperties: true }
expires_at: { type: string, format: date-time }
ArtifactBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [artifact_id, artifact_type]
properties:
type: { type: string, enum: [artifact] }
artifact_id: { type: string }
artifact_type: { type: string }
version: { type: integer }
CitationBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [source_id, title, quoted_text]
properties:
type: { type: string, enum: [citation] }
source_id: { type: string }
title: { type: string }
quoted_text: { type: string }
locator:
type: object
description: 定位(文本区间、图像区域、PDF 页区、音视频时间码)与原文快照
additionalProperties: true
ErrorBlock:
allOf:
- $ref: "#/components/schemas/BlockBase"
- type: object
required: [message]
properties:
type: { type: string, enum: [error] }
message: { type: string }
retryable: { type: boolean }
fallback: { type: string }
ContentBlock:
oneOf:
- $ref: "#/components/schemas/TextBlock"
- $ref: "#/components/schemas/MarkdownBlock"
- $ref: "#/components/schemas/CodeBlock"
- $ref: "#/components/schemas/QuoteBlock"
- $ref: "#/components/schemas/TableBlock"
- $ref: "#/components/schemas/CalloutBlock"
- $ref: "#/components/schemas/MathBlock"
- $ref: "#/components/schemas/MermaidBlock"
- $ref: "#/components/schemas/ImageBlock"
- $ref: "#/components/schemas/VideoBlock"
- $ref: "#/components/schemas/AudioBlock"
- $ref: "#/components/schemas/FileBlock"
- $ref: "#/components/schemas/GalleryBlock"
- $ref: "#/components/schemas/AvatarBlock"
- $ref: "#/components/schemas/ToolCallBlock"
- $ref: "#/components/schemas/ToolResultBlock"
- $ref: "#/components/schemas/AgentStatusBlock"
- $ref: "#/components/schemas/ApprovalRequestBlock"
- $ref: "#/components/schemas/ArtifactBlock"
- $ref: "#/components/schemas/CitationBlock"
- $ref: "#/components/schemas/ErrorBlock"
discriminator:
propertyName: type
# ===== 流事件 envelope(content-blocks-and-streaming 第 5 节) =====
StreamEvent:
type: object
required: [type, data]
properties:
type:
type: string
enum:
[
content.block.started,
content.block.delta,
content.block.progress,
content.block.completed,
content.block.failed,
content.block.cancelled,
message.completed,
]
data: { type: object, additionalProperties: true }
sequence:
type: integer
minimum: 0
description: Run 内单调递增