@tencent-rtc/trtc-agent-skills 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/CODEBUDDY.md +1 -1
- package/README.md +10 -7
- package/README.zh.md +10 -7
- package/bin/cli.js +196 -44
- package/knowledge-base/conference/web/index.yaml +6 -6
- package/knowledge-base/slices/conference/web/official-roomkit-api.md +119 -8
- package/package.json +1 -1
- package/skills/trtc/SKILL.md +45 -10
- package/skills/trtc-ai-oral-coach/README.ja.md +3 -3
- package/skills/trtc-ai-oral-coach/README.md +3 -3
- package/skills/trtc-ai-oral-coach/README.zh-CN.md +3 -3
- package/skills/trtc-ai-oral-coach/SKILL.md +7 -4
- package/skills/trtc-ai-realtime-interpreter/README.ja.md +197 -0
- package/skills/trtc-ai-realtime-interpreter/README.md +197 -0
- package/skills/trtc-ai-realtime-interpreter/README.zh-CN.md +197 -0
- package/skills/trtc-ai-realtime-interpreter/SKILL.md +748 -0
- package/skills/trtc-ai-realtime-interpreter/auto_adapters/integration_templates/generic-rest-api.md +85 -0
- package/skills/trtc-ai-realtime-interpreter/auto_adapters/integration_templates/room-owner-authz-note.md +53 -0
- package/skills/trtc-ai-realtime-interpreter/auto_adapters/manifest.yaml +49 -0
- package/skills/trtc-ai-realtime-interpreter/auto_adapters/python/README.md +11 -0
- package/skills/trtc-ai-realtime-interpreter/auto_adapters/python/fastapi_reverse_proxy.py.tpl +84 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/.env.example +17 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/frontend/silent-listener.ts +91 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/manifest.yaml +110 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/requirements.txt +5 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/src/__init__.py +0 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/src/_capability_loader.py +89 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/src/agent.py +153 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/src/credentials.py +112 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/src/health.py +218 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/src/log_filter.py +33 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/src/server.py +266 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/src/trtc_client.py +188 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/conversation-core/src/usersig.py +53 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/meeting-ops/README.md +46 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/meeting-ops/manifest.yaml +63 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/meeting-ops/src/__init__.py +0 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/meeting-ops/src/fanout.py +149 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/meeting-ops/src/router.py +83 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/realtime-translation/frontend/subtitle-parser.ts +145 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/realtime-translation/manifest.yaml +68 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/realtime-translation/src/__init__.py +0 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/realtime-translation/src/modes.py +73 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/realtime-translation/src/router.py +83 -0
- package/skills/trtc-ai-realtime-interpreter/capabilities/realtime-translation/src/service.py +77 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/README.md +23 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/backend/app/__init__.py +0 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/backend/app/server.py +277 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/backend/requirements.txt +5 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/backend/start.sh +28 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/recipe.yaml +72 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/index.html +12 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/legacy/index.html +738 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/package-lock.json +4302 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/package.json +33 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/postcss.config.js +6 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/App.vue +18 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/api/backend.ts +82 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/components/SetupScreen.vue +362 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/components/SummaryScreen.vue +203 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/components/conference/ChatPanel.vue +188 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/components/conference/ConferenceRoom.vue +453 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/components/conference/ParticipantViewUI.vue +170 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/components/conference/PeoplePanel.vue +206 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/components/conference/SidePanel.vue +77 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/components/conference/Toolbar.vue +371 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/components/conference/TopBar.vue +310 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/components/conference/TranscriptPanel.vue +225 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/composables/useAiInterpreter.ts +263 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/composables/useConference.ts +98 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/env.d.ts +7 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/main.ts +5 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/store.ts +103 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/src/style.css +19 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/tailwind.config.js +25 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/tsconfig.json +25 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/tsconfig.node.json +10 -0
- package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/ui/vite.config.ts +26 -0
- package/skills/trtc-ai-realtime-interpreter/scripts/add-capability.py +163 -0
- package/skills/trtc-ai-realtime-interpreter/scripts/deploy-demo.sh +64 -0
- package/skills/trtc-ai-realtime-interpreter/scripts/lib/__init__.py +0 -0
- package/skills/trtc-ai-realtime-interpreter/scripts/lib/credential_validators.py +143 -0
- package/skills/trtc-ai-realtime-interpreter/scripts/lib/manifest_resolver.py +60 -0
- package/skills/trtc-ai-realtime-interpreter/scripts/lib/stack_detector.py +50 -0
- package/skills/trtc-ai-realtime-interpreter/scripts/post-install-patch.py +79 -0
- package/skills/trtc-ai-realtime-interpreter/scripts/verify-credentials.py +76 -0
- package/skills/trtc-ai-realtime-interpreter/start.sh +85 -0
- package/skills/trtc-ai-realtime-interpreter/triggers.yaml +29 -0
- package/skills/trtc-ai-service/README.ja.md +3 -3
- package/skills/trtc-ai-service/README.md +3 -3
- package/skills/trtc-ai-service/README.zh-CN.md +3 -3
- package/skills/trtc-ai-service/SKILL.md +9 -7
- package/skills/trtc-ai-service/capabilities/conversation-core/src/credentials.py +1 -1
- package/skills/trtc-chat/SKILL.md +1 -1
- package/skills/trtc-chat/docs/SKILL.md +1 -1
- package/skills/trtc-conference/flows/onboarding.md +6 -0
- package/skills/trtc-conference/flows/topic.md +6 -2
- package/skills/trtc-conference/playbooks/official-roomkit.md +3 -1
- package/skills/trtc-conference/tests/test_conference_index_contract.py +16 -0
- package/skills/trtc-conference/tests/test_conference_onboarding_contract.py +9 -0
- package/skills/trtc-conference/tests/test_conference_topic_flow_contract.py +16 -0
- package/skills/trtc-push/SKILL.md +118 -0
- package/skills/trtc-push/issues/ROUTER.json +429 -0
- package/skills/trtc-push/issues/cards/android/fcm-gms-domestic.md +53 -0
- package/skills/trtc-push/issues/cards/android/vendor-huawei.md +72 -0
- package/skills/trtc-push/issues/cards/common/console-certificate-quota.md +46 -0
- package/skills/trtc-push/issues/cards/common/registration-binding.md +67 -0
- package/skills/trtc-push/issues/cards/ios/aps-environment-3000.md +55 -0
- package/skills/trtc-push/issues/cards/ios/certificate-businessid.md +56 -0
- package/skills/trtc-push/issues/cards/ios/xcodegen-cocoapods-module.md +54 -0
- package/skills/trtc-push/issues/flows/android/delivered-not-displayed.md +52 -0
- package/skills/trtc-push/issues/flows/android/vendor-not-received.md +57 -0
- package/skills/trtc-push/issues/flows/common/badge.md +49 -0
- package/skills/trtc-push/issues/flows/common/console-product-limits.md +48 -0
- package/skills/trtc-push/issues/flows/common/server-api.md +51 -0
- package/skills/trtc-push/issues/flows/cross-platform/harmonyos.md +51 -0
- package/skills/trtc-push/issues/flows/cross-platform/uniapp-integration.md +57 -0
- package/skills/trtc-push/issues/flows/ios/offline-not-received.md +57 -0
- package/skills/trtc-push/references/code-templates.md +386 -0
- package/skills/trtc-push/references/hard-rules.md +112 -0
- package/skills/trtc-push/references/timpush-sdk-api.md +72 -0
- package/skills/trtc-push/references/workflow-protocol.md +82 -0
- /package/.cursor/rules/{main.mdc → ui-mode.mdc} +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# meeting-interpreter(Path A 默认场景)
|
|
2
|
+
|
|
3
|
+
会议室 + AI 实时翻译的完整体验 Demo:Vue3 + `tuikit-atomicx-vue3`(Atomicx Core,无预置 UI)搭建真实会议室(登录/建房进房、参会人列表、视频布局、设备、屏幕分享、会中聊天),叠加一层独立的 AI 实时翻译能力(主持人开关 → 按当前真实参会人扇出多路翻译 → 静默旁听转写面板)。
|
|
4
|
+
|
|
5
|
+
## 组成
|
|
6
|
+
|
|
7
|
+
- `backend/` —— 场景专属 FastAPI 后端,把 `conversation-core` + `realtime-translation` + `meeting-ops` 三个能力包粘合成这个 demo 的 API 形状,并实现「仅主持人可开关 AI 翻译」的房主校验(这条规则是本 demo 的产品设计,不属于任何可复用能力包)
|
|
8
|
+
- `ui/` —— Vue3 前端(会议室 Layer 1 + AI 翻译 Layer 2 + 视觉展示层)
|
|
9
|
+
|
|
10
|
+
## 启动
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
cd backend && bash start.sh # 后端:首次运行自动建 venv + 装依赖 + 生成自签证书
|
|
14
|
+
cd ui && npm install && npm run dev # 前端:vite dev,代理 /api 到 https://localhost:8020
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
浏览器打开 `https://localhost:5173`。
|
|
18
|
+
|
|
19
|
+
## 权限说明
|
|
20
|
+
|
|
21
|
+
「仅会议主持人可开关 AI 翻译」是本 demo 的产品规则(起一路 `StartAIConversation` 有云服务调用开销,扇出多路更是倍数开销,不希望任意参会人随手触发)。这条校验写在 `backend/app/server.py` 的 `_verify_owner`,用内存字典记录建房人 = 房主,**仅供演示**。
|
|
22
|
+
|
|
23
|
+
如果你是要把这套能力接入自己已有的系统(而不是照搬这个会议室 demo),请看 Skill 顶层 `auto_adapters/`——那里的集成方式完全不需要这套房主校验:你自己系统的权限体系应该在你自己的后端完成,`meeting-ops` 能力包本身不做任何权限假设。
|
package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/backend/app/__init__.py
ADDED
|
File without changes
|
package/skills/trtc-ai-realtime-interpreter/scenarios/meeting-interpreter/backend/app/server.py
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Path A 场景专属后端:会议室 AI 实时翻译 demo。
|
|
3
|
+
|
|
4
|
+
这是「场景层」代码,不是可复用能力包——它把三个可复用能力包(conversation-core /
|
|
5
|
+
realtime-translation / meeting-ops)粘合成这一个具体 demo 的 API 形状,并且实现了
|
|
6
|
+
demo 专属的产品规则:**仅会议主持人可开关 AI 翻译**。
|
|
7
|
+
|
|
8
|
+
这条权限规则是我们自己拍的产品决策(控制云服务调用开销),不是接入 TRTC 会议能力
|
|
9
|
+
的必要条件,也不属于任何可复用能力包的职责——meeting-ops 本身不做权限校验(见其
|
|
10
|
+
README/manifest 的安全声明)。所以这段 AuthZ glue 代码只放在场景层,Path B 集成方
|
|
11
|
+
接入自己系统时完全不需要参考这套内存字典实现。
|
|
12
|
+
|
|
13
|
+
API 形状:
|
|
14
|
+
GET /api/v1/health —— 三把钥匙自检(代理 conversation-core)
|
|
15
|
+
POST /api/v1/config —— 签发真人 + 静默旁听客户端两套身份 + 语言对列表
|
|
16
|
+
POST /api/v1/room/register —— 房间归属登记(demo 专属,供后续主持人校验)
|
|
17
|
+
POST /api/v1/session/start —— 主持人开启 AI 翻译(校验房主后转发给 meeting-ops 扇出)
|
|
18
|
+
POST /api/v1/session/stop —— 主持人关闭 AI 翻译(校验房主后转发)
|
|
19
|
+
GET /api/v1/session/state —— 查询房间当前 AI 翻译状态(只读,无需权限)
|
|
20
|
+
GET / —— 托管前端构建产物(WEB_DEMO_DIR)
|
|
21
|
+
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import logging
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
import uuid
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any, Dict, List, Optional
|
|
30
|
+
|
|
31
|
+
from dotenv import load_dotenv
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# 路径基线:把三个可复用能力包的 src 目录逐个按需加载(不整体塞进 sys.path,
|
|
35
|
+
# 避免不同能力包内同名文件(如 router.py)互相覆盖;使用 conversation-core 的
|
|
36
|
+
# _capability_loader 做隔离加载)。
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
# 兜底 HTML:dist 缺失时返回的友好提示(避免浏览器看到空白页一脸懵)
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
_MISSING_DIST_HTML = """<!DOCTYPE html>
|
|
41
|
+
<html lang="zh-CN"><head><meta charset="UTF-8"><title>前端资源缺失</title>
|
|
42
|
+
<style>body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;max-width:640px;margin:60px auto;padding:24px;line-height:1.6;color:#1f2937}
|
|
43
|
+
h1{color:#b91c1c}code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:13px}
|
|
44
|
+
ol{padding-left:24px}li{margin:8px 0}</style></head>
|
|
45
|
+
<body>
|
|
46
|
+
<h1>前端资源缺失</h1>
|
|
47
|
+
<p>后端服务已经启动,但 <code>scenarios/meeting-interpreter/ui/dist/</code> 不存在或被删除了。</p>
|
|
48
|
+
<p>Path A 依赖预构建的前端静态资产(dist 由本 Skill 仓库自带,应在 <code>git clone</code> 后立即可见)。</p>
|
|
49
|
+
<p><strong>修复方法(任选其一):</strong></p>
|
|
50
|
+
<ol>
|
|
51
|
+
<li>从 Skill 仓库重新克隆:<code>git clone <repo-url></code>,确保 dist 目录被下载下来</li>
|
|
52
|
+
<li>或重新构建前端:<code>cd scenarios/meeting-interpreter/ui && npm install && npm run build</code></li>
|
|
53
|
+
</ol>
|
|
54
|
+
<p>完成后重启本服务即可。<br>后端 API 仍可用:<a href="/docs">/docs</a> · <a href="/api/v1/health">/api/v1/health</a></p>
|
|
55
|
+
</body></html>"""
|
|
56
|
+
|
|
57
|
+
_HERE = Path(__file__).resolve()
|
|
58
|
+
_SKILL_ROOT = _HERE.parents[4] # scenarios/meeting-interpreter/backend/app/server.py -> skill root
|
|
59
|
+
_CORE_DIR = _SKILL_ROOT / "capabilities" / "conversation-core"
|
|
60
|
+
_CORE_SRC = _CORE_DIR / "src"
|
|
61
|
+
|
|
62
|
+
if str(_CORE_SRC) not in sys.path:
|
|
63
|
+
sys.path.insert(0, str(_CORE_SRC))
|
|
64
|
+
|
|
65
|
+
load_dotenv(_CORE_DIR / ".env")
|
|
66
|
+
|
|
67
|
+
from fastapi import FastAPI, HTTPException, Query # noqa: E402
|
|
68
|
+
from fastapi.middleware.cors import CORSMiddleware # noqa: E402
|
|
69
|
+
from fastapi.responses import FileResponse, HTMLResponse # noqa: E402
|
|
70
|
+
from fastapi.staticfiles import StaticFiles # noqa: E402
|
|
71
|
+
from pydantic import BaseModel # noqa: E402
|
|
72
|
+
|
|
73
|
+
from agent import ConversationAgent # noqa: E402
|
|
74
|
+
from credentials import load_from_env # noqa: E402
|
|
75
|
+
from health import check_all # noqa: E402
|
|
76
|
+
from log_filter import install_redacting_filter # noqa: E402
|
|
77
|
+
from _capability_loader import load_capability # noqa: E402
|
|
78
|
+
|
|
79
|
+
logging.basicConfig(level=os.getenv("LOG_LEVEL", "INFO"), format="%(asctime)s [%(levelname)s] %(name)s: %(message)s")
|
|
80
|
+
install_redacting_filter(logging.getLogger())
|
|
81
|
+
logger = logging.getLogger("meeting-interpreter-scenario")
|
|
82
|
+
|
|
83
|
+
_credentials = load_from_env()
|
|
84
|
+
_agent: Optional[ConversationAgent] = None
|
|
85
|
+
_init_error: Optional[str] = None
|
|
86
|
+
try:
|
|
87
|
+
_agent = ConversationAgent(_credentials)
|
|
88
|
+
logger.info("ConversationAgent initialized")
|
|
89
|
+
except Exception as exc: # noqa: BLE001
|
|
90
|
+
_init_error = str(exc)
|
|
91
|
+
logger.warning("ConversationAgent not initialized: %s", _init_error)
|
|
92
|
+
|
|
93
|
+
# 动态加载 meeting-ops 的扇出编排器 + realtime-translation 的语言对列表
|
|
94
|
+
_fanout_mod = load_capability("meeting-ops", "src/fanout.py")
|
|
95
|
+
_modes_mod = load_capability("realtime-translation", "src/modes.py")
|
|
96
|
+
_orchestrator = _fanout_mod.get_orchestrator()
|
|
97
|
+
|
|
98
|
+
app = FastAPI(title="meeting-interpreter-scenario", version="1.0.0")
|
|
99
|
+
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
|
|
100
|
+
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
# demo 专属内存态:房间归属登记(AuthZ 基准,仅场景层持有,不属于任何能力包)
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
_ROOM_OWNERS: Dict[str, str] = {}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _require_agent() -> ConversationAgent:
|
|
108
|
+
if _agent is None:
|
|
109
|
+
raise HTTPException(status_code=503, detail={
|
|
110
|
+
"code": "credentials_missing",
|
|
111
|
+
"message": _init_error or "credentials not configured",
|
|
112
|
+
"hint": "在 capabilities/conversation-core/.env 里配好三把钥匙后重启",
|
|
113
|
+
})
|
|
114
|
+
return _agent
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _verify_owner(room_id: str, caller_user_id: str) -> None:
|
|
118
|
+
"""服务端独立校验调用者是否为该房间主持人(不信任前端自报)。
|
|
119
|
+
|
|
120
|
+
这是 demo 专属的产品规则实现,不代表接入 meeting-ops 必须要有这层校验——
|
|
121
|
+
Path B 集成方应该用自己系统里真实的权限判断替换这里的逻辑。
|
|
122
|
+
"""
|
|
123
|
+
owner = _ROOM_OWNERS.get(room_id)
|
|
124
|
+
if not owner:
|
|
125
|
+
raise HTTPException(status_code=403, detail={"code": "room_not_registered", "message": "房间未登记归属,无法校验主持人权限"})
|
|
126
|
+
if owner != caller_user_id:
|
|
127
|
+
raise HTTPException(status_code=403, detail={"code": "not_owner", "message": "仅会议主持人可操作 AI 实时翻译"})
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# ---------------------------------------------------------------------------
|
|
131
|
+
@app.get("/api/v1/health")
|
|
132
|
+
def health() -> Dict[str, Any]:
|
|
133
|
+
cred = load_from_env()
|
|
134
|
+
tc, trtc, llm = check_all(cred.tencent_cloud, cred.trtc, cred.llm)
|
|
135
|
+
missing = cred.missing()
|
|
136
|
+
return {
|
|
137
|
+
"status": "ok" if (tc.ok and trtc.ok and llm.ok) else "missing_credentials",
|
|
138
|
+
"trtc_configured": cred.trtc.configured,
|
|
139
|
+
"tencent_configured": cred.tencent_cloud.configured,
|
|
140
|
+
"llm_configured": cred.llm.configured,
|
|
141
|
+
"missing": missing,
|
|
142
|
+
"modes": list(_modes_mod.MODE_CONFIG.keys()),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# ---------------------------------------------------------------------------
|
|
147
|
+
class RoomRegisterRequest(BaseModel):
|
|
148
|
+
roomId: str
|
|
149
|
+
ownerUserId: str
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@app.post("/api/v1/room/register")
|
|
153
|
+
def register_room(body: RoomRegisterRequest) -> Dict[str, Any]:
|
|
154
|
+
_ROOM_OWNERS[body.roomId] = body.ownerUserId
|
|
155
|
+
logger.info("room registered: %s -> owner %s", body.roomId, body.ownerUserId)
|
|
156
|
+
return {"ok": True, "roomId": body.roomId, "ownerUserId": body.ownerUserId}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# ---------------------------------------------------------------------------
|
|
160
|
+
class ConfigRequest(BaseModel):
|
|
161
|
+
userid: Optional[str] = None
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@app.post("/api/v1/config")
|
|
165
|
+
def issue_config(body: ConfigRequest) -> Dict[str, Any]:
|
|
166
|
+
agent = _require_agent()
|
|
167
|
+
user_id = (body.userid or f"host_{uuid.uuid4().hex[:8]}")[:32]
|
|
168
|
+
# 旁听客户端:独立身份进同一房间,只收自定义消息,不推流(见 conversation-core/frontend/silent-listener.ts)
|
|
169
|
+
listener_user_id = f"{user_id}_lsnr"[:32]
|
|
170
|
+
user_sig = agent.issue_user_sig(user_id)
|
|
171
|
+
listener_user_sig = agent.issue_user_sig(listener_user_id)
|
|
172
|
+
return {
|
|
173
|
+
"sdkappid": agent.sdk_app_id,
|
|
174
|
+
"userid": user_id,
|
|
175
|
+
"usersig": user_sig,
|
|
176
|
+
"listener_userid": listener_user_id,
|
|
177
|
+
"listener_usersig": listener_user_sig,
|
|
178
|
+
"modes": _modes_mod.list_modes(),
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# ---------------------------------------------------------------------------
|
|
183
|
+
class SessionStartRequest(BaseModel):
|
|
184
|
+
roomId: str
|
|
185
|
+
roomIdType: int = 1
|
|
186
|
+
callerUserId: str
|
|
187
|
+
mode: str
|
|
188
|
+
participants: List[str]
|
|
189
|
+
ttsEnabled: bool = False
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@app.post("/api/v1/session/start")
|
|
193
|
+
def session_start(body: SessionStartRequest) -> Dict[str, Any]:
|
|
194
|
+
agent = _require_agent()
|
|
195
|
+
_verify_owner(body.roomId, body.callerUserId)
|
|
196
|
+
try:
|
|
197
|
+
result = _orchestrator.start(
|
|
198
|
+
agent=agent,
|
|
199
|
+
room_id=body.roomId,
|
|
200
|
+
participants=body.participants,
|
|
201
|
+
capability="realtime-translation",
|
|
202
|
+
params={"mode": body.mode, "tts_enabled": body.ttsEnabled},
|
|
203
|
+
room_id_type=body.roomIdType,
|
|
204
|
+
)
|
|
205
|
+
except RuntimeError as exc:
|
|
206
|
+
# fanout 全部失败时,返回 500 + 错误详情(而不是 200 空数组让前端以为成功)
|
|
207
|
+
logger.error("session_start failed: %s", exc)
|
|
208
|
+
raise HTTPException(status_code=500, detail={
|
|
209
|
+
"code": "fanout_failed",
|
|
210
|
+
"message": str(exc),
|
|
211
|
+
"hint": "常见原因:①SDKSecretKey 填成了 STSecretKey ②SDKAppID 与密钥不匹配 ③TRTC 应用未开通对话式 AI",
|
|
212
|
+
})
|
|
213
|
+
return {
|
|
214
|
+
"active": result["active"],
|
|
215
|
+
"mode": body.mode,
|
|
216
|
+
"bots": [{"botUserId": b["agentUserId"], "targetUserId": b["targetUserId"]} for b in result["bots"]],
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class SessionStopRequest(BaseModel):
|
|
221
|
+
roomId: str
|
|
222
|
+
callerUserId: str
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@app.post("/api/v1/session/stop")
|
|
226
|
+
def session_stop(body: SessionStopRequest) -> Dict[str, Any]:
|
|
227
|
+
agent = _require_agent()
|
|
228
|
+
_verify_owner(body.roomId, body.callerUserId)
|
|
229
|
+
stopped = _orchestrator.stop(agent, body.roomId)
|
|
230
|
+
return {"ok": True, "stopped": stopped}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@app.get("/api/v1/session/state")
|
|
234
|
+
def session_state(roomId: str = Query(...)) -> Dict[str, Any]:
|
|
235
|
+
st = _orchestrator.state(roomId)
|
|
236
|
+
return {
|
|
237
|
+
"active": st["active"],
|
|
238
|
+
"mode": (st.get("params") or {}).get("mode"),
|
|
239
|
+
"bots": [{"botUserId": b["agentUserId"], "targetUserId": b["targetUserId"]} for b in st["bots"]],
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# ---------------------------------------------------------------------------
|
|
244
|
+
# 静态前端:默认托管 ../ui/dist(Vite 构建产物),可用 FRONTEND_DIR 覆盖
|
|
245
|
+
# ---------------------------------------------------------------------------
|
|
246
|
+
_FRONTEND_DIR = Path(os.getenv("FRONTEND_DIR") or (Path(__file__).resolve().parents[2] / "ui" / "dist"))
|
|
247
|
+
if _FRONTEND_DIR.exists():
|
|
248
|
+
# 把预构建的 dist 挂到根路径(index.html 引用 /assets/xxx.js,所以必须根挂载)
|
|
249
|
+
# FastAPI 的 /api/* 显式路由优先级高于 mount,/docs /openapi.json 等也都能正常访问
|
|
250
|
+
app.mount("/", StaticFiles(directory=str(_FRONTEND_DIR), html=True), name="dist")
|
|
251
|
+
|
|
252
|
+
@app.get("/")
|
|
253
|
+
def index():
|
|
254
|
+
p = _FRONTEND_DIR / "index.html"
|
|
255
|
+
if p.exists():
|
|
256
|
+
return FileResponse(str(p))
|
|
257
|
+
# 兜底:dist 不存在时给个明确的修复提示(避免浏览器白屏一脸懵)
|
|
258
|
+
return HTMLResponse(_MISSING_DIST_HTML, status_code=503)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def main() -> None:
|
|
262
|
+
import uvicorn
|
|
263
|
+
|
|
264
|
+
cert_dir = Path(__file__).resolve().parents[1] # scenarios/meeting-interpreter/backend/
|
|
265
|
+
cert_file = cert_dir / "cert.pem"
|
|
266
|
+
key_file = cert_dir / "key.pem"
|
|
267
|
+
port = int(os.getenv("PORT", "8020"))
|
|
268
|
+
if cert_file.exists() and key_file.exists():
|
|
269
|
+
logger.info("Starting HTTPS server (cert=%s)", cert_file)
|
|
270
|
+
uvicorn.run(app, host=os.getenv("HOST", "0.0.0.0"), port=port, ssl_certfile=str(cert_file), ssl_keyfile=str(key_file))
|
|
271
|
+
else:
|
|
272
|
+
logger.info("Starting HTTP server (no cert.pem/key.pem found)")
|
|
273
|
+
uvicorn.run(app, host=os.getenv("HOST", "0.0.0.0"), port=port)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
if __name__ == "__main__":
|
|
277
|
+
main()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
cd "$(dirname "$0")"
|
|
4
|
+
|
|
5
|
+
if [ ! -d ".venv" ]; then
|
|
6
|
+
echo "[start.sh] creating venv..."
|
|
7
|
+
python3 -m venv .venv
|
|
8
|
+
fi
|
|
9
|
+
source .venv/bin/activate
|
|
10
|
+
pip install -q -r requirements.txt
|
|
11
|
+
|
|
12
|
+
# 三把钥匙统一放在 capabilities/conversation-core/.env(skill 唯一真源)
|
|
13
|
+
CORE_ENV="../../../capabilities/conversation-core/.env"
|
|
14
|
+
if [ ! -f "$CORE_ENV" ]; then
|
|
15
|
+
echo "[start.sh] $CORE_ENV not found, copying from .env.example (请去里面填三把钥匙)"
|
|
16
|
+
cp "../../../capabilities/conversation-core/.env.example" "$CORE_ENV"
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# 自动生成自签名 HTTPS 证书(TRTC Web SDK 要求 HTTPS 才能采集麦克风/摄像头)
|
|
20
|
+
if [ ! -f "cert.pem" ] || [ ! -f "key.pem" ]; then
|
|
21
|
+
echo "[start.sh] generating self-signed TLS cert (cert.pem / key.pem) ..."
|
|
22
|
+
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes \
|
|
23
|
+
-subj "/CN=localhost" 2>/dev/null || {
|
|
24
|
+
echo "[start.sh] WARNING: openssl 生成证书失败,将以 HTTP 模式启动(只能用 localhost 访问)"
|
|
25
|
+
}
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
python3 -m app.server
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# =====================================================================
|
|
2
|
+
# Path A 默认配方 —— AI 会议实时翻译(Vue3 + Atomicx Core 会议室 + 实时翻译叠加层)
|
|
3
|
+
#
|
|
4
|
+
# 本文件由 SKILL.md 的 Path A SOP 解析执行;不是 add-capability.py 的输入。
|
|
5
|
+
# Coding Agent 按顺序逐条执行作为"组装说明清单",用户不直接运行本文件。
|
|
6
|
+
# =====================================================================
|
|
7
|
+
apiVersion: trtc-ai-realtime-interpreter/v1
|
|
8
|
+
kind: Recipe
|
|
9
|
+
metadata:
|
|
10
|
+
name: meeting-interpreter-default
|
|
11
|
+
scenario: meeting-interpreter
|
|
12
|
+
version: "1.0.0"
|
|
13
|
+
description: "默认会议实时翻译 Demo(Vue3 Atomicx 会议室 + AI 翻译叠加层 + 房主权限控制 + 静默旁听转写面板)"
|
|
14
|
+
language: "zh"
|
|
15
|
+
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
# 能力组装清单
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
capabilities:
|
|
20
|
+
required:
|
|
21
|
+
- name: conversation-core
|
|
22
|
+
role: skeleton
|
|
23
|
+
- name: realtime-translation
|
|
24
|
+
role: capability
|
|
25
|
+
- name: meeting-ops
|
|
26
|
+
role: capability
|
|
27
|
+
scenario_glue:
|
|
28
|
+
# 房主权限校验属于场景层专属实现,不属于任何可复用能力包
|
|
29
|
+
- description: "内存字典房间归属登记 + 主持人 AuthZ 校验"
|
|
30
|
+
location: "backend/app/server.py"
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# 部署参数
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
deploy:
|
|
36
|
+
backend:
|
|
37
|
+
entry: "backend/start.sh"
|
|
38
|
+
port_env: "PORT"
|
|
39
|
+
default_port: 8020
|
|
40
|
+
https: true # TRTC Web SDK 采集麦克风要求安全上下文
|
|
41
|
+
frontend:
|
|
42
|
+
dev_command: "npm install && npm run dev"
|
|
43
|
+
build_command: "npm install && npm run build"
|
|
44
|
+
dev_port: 5173
|
|
45
|
+
proxy_target_env: "VITE_BACKEND_TARGET"
|
|
46
|
+
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
# 启动后访问入口
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
routes:
|
|
51
|
+
- path: "https://localhost:5173"
|
|
52
|
+
description: "前端开发模式入口(vite dev,代理 /api 到后端)"
|
|
53
|
+
- path: "https://localhost:8020"
|
|
54
|
+
description: "后端生产模式入口(托管 ui/dist 构建产物)"
|
|
55
|
+
- path: "https://localhost:8020/api/v1/health"
|
|
56
|
+
description: "三把钥匙自检 JSON"
|
|
57
|
+
- path: "https://localhost:8020/docs"
|
|
58
|
+
description: "FastAPI Swagger"
|
|
59
|
+
|
|
60
|
+
post_assembly:
|
|
61
|
+
health_check:
|
|
62
|
+
url: "https://localhost:8020/api/v1/health"
|
|
63
|
+
expect_field: "status"
|
|
64
|
+
expect_value: "ok"
|
|
65
|
+
|
|
66
|
+
go_production_guide: |
|
|
67
|
+
- 房主权限校验:backend/app/server.py 里的 _ROOM_OWNERS 内存字典仅供演示,
|
|
68
|
+
生产环境请替换为你自己会议系统里真实的房主/管理员判断逻辑。
|
|
69
|
+
- 房间级会话状态(meeting-ops 内存态)在多实例部署下不共享,如需水平扩展,
|
|
70
|
+
自行接入 Redis 等外部状态存储替换 fanout.py 的内存字典。
|
|
71
|
+
- 端口:默认后端 8020 / 前端开发 5173;生产建议前端构建后由后端统一托管。
|
|
72
|
+
- HTTPS:start.sh 会自动生成自签名证书;生产环境请用真实证书的反向代理替换。
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
6
|
+
<title>Conference · with AI Live Translator</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body class="bg-background text-foreground font-sans overflow-x-hidden antialiased">
|
|
9
|
+
<div id="app"></div>
|
|
10
|
+
<script type="module" src="/src/main.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|