Skip to content

Viewer REST API ​

后端地址 http://localhost:8090。除静态文件端点外,响应统一为 {code, message, data},code=0 表示成功。模型 id 格式是 m_ 加 16 位小写 hex。

完整的机器可读 schema 见 go-server.openapi.json,由脚本生成并做路由覆盖检测,详见文末机器可消费 OpenAPI一节。

模型 ​

POST /api/v1/models ​

上传 IFC 文件并触发异步转换。请求是 multipart/form-data,字段 file,仅接受 .ifc,上限 200MB。

用户界面的上传入口已于 2026-08-21 隐藏,模型由 agent 在项目会话内生成。本端点保留为 agent 建模型的内部链路,用户不需要直接调它。

响应:

json
{"code":0,"message":"ok","data":{"id":"m_01J...","name":"Building-Architecture.ifc","status":"converting"}}

错误:40001 文件类型非法,40002 超出大小上限。

GET /api/v1/models ​

模型列表,前端每 2 秒轮询,直到所有模型脱离 converting:

json
{"code":0,"message":"ok","data":[
  {"id":"m_01J...","name":"a.ifc","size":1832140,"status":"ready","createdAt":"2026-07-27T10:00:00Z","error":""}
]}

status 取值 converting、ready、failed。

GET /api/v1/models/{id} ​

单模型详情,结构同上。

POST /api/v1/models/{id}/retry ​

对 failed 模型重新入队转换,返回更新后的模型对象。

DELETE /api/v1/models/{id} ​

删除该模型的 IFC、XKT、metadata、状态文件及 issues、changes、overrides。

模型归属于项目时,删除会自动把它从项目里摘掉,project.json 同步更新,不会留下孤立的 modelId。

GET /api/v1/models/{id}/download ​

下载原始 IFC,响应带 Content-Disposition: attachment。

Issues ​

issue id 格式是 i_ 加 12 位小写 hex。status 取值 open、checking、resolved。作者默认 local-user,来源默认 UI,创建时可覆盖。

GET /api/v1/models/{id}/issues ​

返回 data: Issue[],按创建时间降序。

POST /api/v1/models/{id}/issues ​

multipart/form-data 两个部分:

  • issue(必填):JSON 字符串,含 entityId、entityName、entityType、title(必填)、comment、可选的 author、provenance 和相机参数 camera。
  • screenshot(可选):PNG,上限 5MB。

返回创建后的 data: Issue,含生成的 id、初始状态和截图相对路径。

PATCH /api/v1/models/{id}/issues/{issueId} ​

JSON body 传 title、comment、status 中要更新的字段。

DELETE /api/v1/models/{id}/issues/{issueId} ​

删除 Issue 及其截图。

GET /v1/models/{id}/issues/ ​

Issue 截图静态服务,file 必须匹配 i_[0-9a-f]{12}\.png。

属性 Override 与修改记录 ​

属性修改走 metadata override,不改 IFC 本体。白名单字段只有 Name、Description、Classification、FireRating、Comments,每次修改逐字段写一条 change log。

GET /api/v1/models/{id}/overrides ​

返回 data: { [entityId]: { [field]: value } },无数据时是 {}。

PUT /api/v1/models/{id}/entities/{entityId}/properties ​

JSON body:

json
{"entityName":"Wall","fields":{"FireRating":"F60","Comments":"备注"}}

fields 必填且非空,字段名不在白名单返回 40001。空字符串表示清除该字段的 override。每个字段写一条 change log,返回该实体当前生效的 override 集合。

GET /api/v1/models/{id}/changes ​

返回 data: ChangeEntry[],按创建时间降序:

json
{"code":0,"message":"ok","data":[
  {"id":"c_1a2b3c4d5e6f","entityId":"3a82-xxxx","entityName":"Wall","field":"FireRating","oldValue":"","newValue":"F60","author":"local-user","provenance":{"source":"UI"},"operation":"update","createdAt":"2026-07-29T10:00:00Z"}
]}

Chat:项目、会话与 AI 对话 ​

进程内 chat agent 的 REST 与 SSE 接口。项目是入口,一个项目绑定唯一会话;前端的历史项目列表就是会话列表。

项目 ​

POST /api/v1/chat/projects ​

创建项目。body 是 {"title", "kind"},kind 必填,取值 ifc、cad、cad->ifc,它决定 agent 的派发方向。

模型初始化按 kind 分化:ifc 建项目时就生成骨架模型,跑一遍最小脚本产出 v1;cad 和 cad->ifc 先留空,agent 会话内用 init_model 按需建。

项目创建和删除在 /api/v1/chat/projects,方案读写在 /api/v1/projects/{id}/...。同一个项目 id 出现在两个前缀下是历史原因。

json
{"code":0,"message":"ok","data":{"projectId":"p_xxxx","title":"我的项目","kind":"cad","createdAt":"2026-08-21T06:00:00Z","models":[]}}

错误:40001 kind 缺失或非法。

会话 ​

GET /api/v1/chat/sessions ​

会话列表:

json
{"code":0,"message":"ok","data":[
  {"chatSessionId":"c_xxxx","opencodeSessionId":"s_xxxx","modelId":"","projectId":"p_xxxx","title":"我的项目","createdAt":"2026-08-21T06:00:00Z"}
]}

opencodeSessionId 是历史遗留字段名,契约如此。

DELETE /api/v1/chat/projects/{id} ​

删除项目并级联清理:会话、事件日志、方案文件、项目下所有模型及其附属数据。注意和单模型删除的区别——删项目就是删全套。

POST /api/v1/chat/sessions ​

创建或复用会话。传 {"title", "projectId"} 是项目级语义,projectId 幂等,一个项目只有一个会话;传 {"title", "modelId"} 是旧的单模型语义。错误:40001 项目不存在。

POST /api/v1/chat/sessions/{cid}/messages ​

发消息,异步处理。body 是 {"text"},响应 {"accepted":true},事件经 SSE 推送。

GET /api/v1/chat/sessions/{cid}/messages ​

会话历史,前端按消息 id 去重合并。

GET /api/v1/chat/sessions/{cid}/events ​

SSE 事件流。帧类型:

eventdata说明
session.statusbusy 或 idle一轮对话的边界
message.updated消息骨架
message.part.updatedpart 定型文本、推理、工具卡片
message.part.delta流式增量
subagent.statussubagentId、persona、status、task子 agent 边界
question.askinterruptId、questionagent 向用户提问
viewer.stagedmodelId、kind试运行成功的中途预览
viewer.committed保存成功驱动前端刷新
model.createdmodelId、kind、title、projectIdagent 创建了新模型
session.errorerror错误
session.idle空本轮结束

支持 Last-Event-ID 断线重同步,缓冲最近 64 条。

POST /api/v1/chat/sessions/{cid}/answer ​

回答 agent 的提问,body 是 {"interruptId", "answer"},回答后 agent 继续执行。

POST /api/v1/chat/sessions/{cid}/abort ​

中止当前执行。

项目级方案产物 ​

GET / PUT /api/v1/projects/{projectID}/{name} ​

方案文件读写。name 取值 plan、bim_supplement、building。PUT 全量替换并写入版本历史。

三个方案文件走 PlanStore 版本化。skill 的中间产物如 design.json 不版本化,落在 skill 工作区,随项目删除清理。

GET /api/v1/projects/{projectID}/plan_history ​

方案版本历史。

GET /api/v1/projects/{projectID}/plan_history/{base}/{target}/diff ​

两个方案版本的 diff,base 和 target 可以是 v{n} 或 current。

POST /api/v1/projects/{projectID}/deliver ​

方案交付,body 是 {"plan", "bimSupplement"}。

编辑代理端点 ​

Go server 把编辑服务的脚本端点暴露在 /api/v1/models/{id}/script/...,run、save、rollback 成功后自动排队重转 XKT。只读与对比端点在 /api/v1/models/{id}/edit/... 前缀下。完整契约见 IFC 编辑 API。

静态资源 ​

以下路径直接返回文件,不走 JSON envelope:

路径说明
GET /v1/models/{id}/model.xktXKT 几何数据,支持 Range
GET /v1/models/{id}/metadata.jsonxeokit 元模型,schema 见下
GET /v1/models/{id}/render.jsonCAD 渲染数据,仅 dxf 模型,schema 见下
GET /v1/models/{id}/issues/{file}Issue 截图

metadata.json Schema ​

由 converter 从 IFC 提取,含空间结构树和属性集,可直接作为 XKTLoaderPlugin.load 的输入:

json
{
  "projectId": "3xFoo",
  "metaObjects": [
    {"id": "1AbC...", "type": "IfcBuildingStorey", "name": "Level 1", "parent": "0Root"},
    {"id": "2XdE...", "type": "IfcWall", "name": "Wall-001", "parent": "1AbC...", "propertySetIds": ["pset_2XdE_0"]}
  ],
  "propertySets": [
    {
      "id": "pset_2XdE_0",
      "name": "Pset_WallCommon",
      "type": "Pset",
      "properties": [
        {"name": "FireRating", "value": "120min", "type": "IfcLabel"},
        {"name": "LoadBearing", "value": true, "type": "IfcBoolean"}
      ]
    }
  ]
}

metaObjects[].id 就是 IFC GlobalId,与 XKT 实体 id 一致。层级为 Site、Building、Storey 到构件。没有 pset 的构件省略 propertySetIds。

render.json Schema ​

由 services/cad 在 run 或 save 成功后原子发布,供前端 Canvas 二维预览。坐标保留原始 DXF 坐标系:

json
{
  "schemaVersion": 2,
  "bounds": {"min": [0, 0], "max": [100, 80]},
  "layers": [{"name": "WALL", "color": 7, "linetype": "CONTINUOUS"}],
  "entities": [
    {"key": "e_1a2b3c", "type": "LINE", "layer": "WALL", "start": [0, 0], "end": [10, 0]}
  ],
  "unsupported": [{"type": "HATCH", "handle": "1F", "coords": [5, 5]}]
}

约定:bounds 在没有可用坐标时为 null;entities[].key 是 XDATA 稳定 key,前端选中实体即得到 key;LWPOLYLINE 炸开成 LINE 和 ARC 条目;INSERT 只展开一层,子实体 key 为 null;白名单外的实体明面列入 unsupported,不静默丢弃。

通用错误码 ​

40001 参数校验错误,40002 超限,40400 不存在,50000 内部错误。

机器可消费 OpenAPI ​

edit-service ​

完整 schema 见 ai-tools.openapi.json,由 FastAPI 直接导出,与运行中服务的 GET /openapi.json 一致;渲染成文档的版本见编辑 API 参考。编辑 API 变更后重新生成:

bash
cd services/ifc
uv run python scripts/export_openapi.py   # 输出到 docs/site/public/ai-tools.openapi.json

Go server ​

完整 schema 见 go-server.openapi.json,可以直接喂给 LLM、工具或代码生成器。说明一点:Go 的标准库 mux 没有反射,schema 无法从代码自动导出,因此采用三层机制——路由清单由脚本从 mux 注册自动提取,请求响应 schema 手工维护,生成器对两者做双向覆盖断言。新增路由没配 schema,或 schema 里有已删除的路由,生成都会失败,CI 会红。

bash
cd docs
npm run gen:api    # 生成三件产物:edit-api-reference.md、go-rest-api.routes.json、go-server.openapi.json
npm run check:api  # 自证测试 + 生成 + git 漂移检测,无 diff 才绿

Apache-2.0 · xeokit AGPL 注意事项见项目介绍