ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Friend 开源仓库插件体系深度解析:共享 SDK 模型、独立 FastAPI 插件服务与遗留单体架构

Friend 开源仓库插件体系深度解析:共享 SDK 模型、独立 FastAPI 插件服务与遗留单体架构 Friend 开源仓库插件体系深度解析共享 SDK 模型、独立 FastAPI 插件服务与遗留单体架构【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend本篇技术指南围绕plugins/目录的权威说明文档plugins/README.md展开系统梳理 Friend 开源项目中插件体系的三层结构模型共享的omi-plugin-sdk、可独立部署的 28 个omi-*-app/FastAPI 服务以及仅因既有部署目标而保留的遗留单体main.py Dockerfile。读完本文你将掌握 Omi webhook 载荷模型的字段语义、SDK 与各消费方的依赖安装方式、独立服务的部署描述符Dockerfile / Procfile / railway.toml形态以及遗留单体的路由构成与维护红线可直接用于插件开发、迁移或审计。一、目录全景plugins/里装的是三件不同的事plugins/不是一个单一的应用而是三类性质完全不同的代码的集合这一点在 plugins/README.md 的开篇就明确声明omi-plugin-sdk/——共享 Python 包只负责 Omi webhook 载荷模型Conversation、TranscriptSegment、ActionItem等不包含任何运行时业务逻辑omi-*-app/——28 个彼此独立部署的插件服务notion、github、slack、dropbox、whoop……每个目录自包含main.py、依赖文件和部署描述符遗留单体main.py Dockerfile——旧的一站式插件 API保留的唯一原因是其 Cloud Run 部署目标仍然存在。理解这种共享模型 独立服务 遗留单体的混合形态是进入 Friend 插件生态的第一步模型层做契约独立服务做业务单体只做历史兼容。二、omi-plugin-sdk仅含模型的共享契约层2.1 设计定位与历史演进omi-plugin-sdk是一个很小的 Python 包其职责被刻意收窄为只拥有 Omi webhook 载荷模型omi_plugin_sdk.models下的Conversation、TranscriptSegment、ActionItem等。根据 plugins/README.md 的记载原本包含的 auth / webhook / FastAPI 辅助模块已于 2026 年 7 月被移除原因是零调用方的投机性代码审计文档 plugins/PLUGIN_REFACTOR_AUDIT.md 补充确认删除发生在提交f6ac87773f如今 SDK 的源码仅剩__init__.py与models.py两个文件。从 plugins/omi-plugin-sdk/pyproject.toml 可以看出包的关键元数据[project] name omi-plugin-sdk version 0.1.0 description Shared Omi plugin webhook models and small integration primitives. requires-python 3.10 dependencies [pydantic2.0.0] [tool.setuptools.packages.find] where [src]依赖仅pydantic2.0.0全部模型基于 Pydantic v2 构建包源码采用src/布局导入路径为omi_plugin_sdk.models。2.2 SDK 安装方式按消费方不同而不同SDK 的安装方式并不统一取决于谁在使用它这是 plugins/README.md 明确强调的要点遗留单体plugins/main.py plugins/Dockerfile通过 plugins/requirements.txt 安装 SDK该文件第一行就是路径依赖./omi-plugin-sdk独立部署的omi-*-app/服务每个服务有各自的requirements.txt在自己目录内通过相对路径../omi-plugin-sdk安装 SDK不经过plugins/requirements.txt。独立服务之所以用相对路径是因为这些服务通常以仓库 checkout 形式构建见后文 notion 示例plugins/omi-plugin-sdk与 app 目录互为同级。审计文档 plugins/PLUGIN_REFACTOR_AUDIT.md 特别警示了这一模式的依赖风险如果某个服务以隔离根目录方式构建不包含同级 SDK 目录依赖安装就会失败——这也是当年不做破坏性迁移的原因之一。SDK 自己的 plugins/omi-plugin-sdk/README.md 给出了本地开发安装与规范导入方式pip install -e plugins/omi-plugin-sdkfrom omi_plugin_sdk.models import ( ActionItem, Conversation, ConversationPhoto, EndpointResponse, Event, Structured, TranscriptSegment, )2.3 核心载荷模型逐字段拆解模型实现的完整源码位于 plugins/omi-plugin-sdk/src/omi_plugin_sdk/models.py下面按 webhook 数据流自上而下拆解关键模型。Conversation—— 会话根对象class Conversation(BaseModel): id: Optional[str] None created_at: datetime started_at: Optional[datetime] None finished_at: Optional[datetime] None transcript_segments: List[TranscriptSegment] Field(default_factorylist) photos: Optional[List[ConversationPhoto]] Field(default_factorylist) structured: Structured apps_results: List[AppResult] Field(default_factorylist) plugins_results: List[PluginResult] Field(default_factorylist) discarded: bool FalseConversation是 webhook 载荷的顶层容器聚合了转写片段、照片、结构化笔记与应用/插件结果。值得注意的实现细节它带有一个model_validator(modeafter)的sync_plugin_results当apps_results非空而plugins_results为空时自动把AppResult转换为PluginResult(plugin_idapp.app_id, contentapp.content)实现新旧结果字段的兼容便捷方法get_transcript(include_timestamps, user_name)直接委托TranscriptSegment.segments_as_string生成可读文本get_duration()取所有片段start的最小值与end的最大值之差格式化为HH:MM:SS字符串。TranscriptSegment—— 转写片段class TranscriptSegment(BaseModel): id: Optional[str] None text: str speaker: Optional[str] SPEAKER_00 speaker_id: Optional[int] None is_user: bool person_id: Optional[str] None start: float end: floatTranscriptSegment是插件处理转写的基本单位。源码为其实现了四个静态/实例工具直接服务于插件的转写处理speaker_id自动推导重写的__init__会从speaker字段形如SPEAKER_03解析出数字序号写入speaker_id解析失败时兜底为 0get_timestamp_string()把start/end秒数转成00:01:23 - 00:01:45样式的可读时间戳segments_as_string(...)把片段列表渲染成User: .../Speaker N: ...的对话文本可用user_name参数替换用户显示名include_timestamps仅在片段时间无重叠时启用见can_display_secondscombine_segments(...)增量合并片段支持delta_seconds时间偏移相邻片段若说话人相同或同为用户文本直接拼接并延伸end时间合并后还会做一次文本清洗去掉多余空格、修正,/./?等标点间距。ActionItem—— 行动项ActionItem是插件提取待办的核心模型字段设计体现了相当成熟的意图理解能力基础字段description行动项内容、completed、created_at/updated_at/due_at/completed_at、conversation_id捕获语义capture_kind枚举explicit_command显式指令/clear_commitment明确承诺/direct_request直接请求/inferred_next_step推断的下一步capture_confidence与ownership_confidence均为0~1的置信度归属语义capture_owner枚举user/other/unknownowner_name记录已知归属人姓名上下文与确定性context用一句话说明该行动项为何重要due_certainty区分confirmed/tentativeconcrete_deliverable仅在承诺涉及具体交付物/结果时为真任务联动candidate_actioncreate/update/complete与target_task_id用于与任务系统双向同步source_segment_ids回指支撑它的转写片段。静态方法actions_to_string会把行动项列表格式化为- 描述 (pending|completed) [Created: ...] [Due: ...]的文本便于直接拼进 LLM prompt 或通知。Event与Structured—— 结构化笔记Event表示从对话中提取的日历事件title、description、start、duration分钟数、createdas_dict_cleaned_dates会把start序列化为 ISO 字符串。Structured是整段对话的结构化总结字段包括title、overview、emoji、category、sectionsSection为标题 Markdown 正文 支撑片段 ID的笔记小节、action_items与events。其category字段由CategoryEnum约束枚举覆盖 personal、education、health、finance、legal、work、sports、technology、business 等三十余个分类并带一个modebefore的字段校验器非法分类值会被静默兜底为CategoryEnum.other保证下游容错。__str__则把标题、分类、概览、行动项与事件渲染为统一文本。其余模型ConversationPhoto会话照片base64 内容 描述 创建时间 discarded标记photos_as_string用于把照片描述渲染为文本列表PluginResult/AppResult插件/应用运行结果仅含plugin_id/app_id与content文本Geolocation、ExternalIntegrationCreateConversation、ExternalIntegrationConversationSource供外部集成以文本创建会话的入参模型text_source区分audio_transcript与other_textEndpointResponsewebhook 的标准响应只有一个message字段——如需通知用户的一条短消息默认空字符串。2.4 SDK 模型的测试保障SDK 自带测试 plugins/omi-plugin-sdk/tests/test_models.py与models.py同处一个包内。对于任何新增插件直接复用这套模型即可保证 webhook 载荷解析与后端行为一致无需在插件内重复定义Structured/ActionItem/Event这正是 plugins/PLUGIN_REFACTOR_AUDIT.md 中Before → After对照表所解决的问题此前同一组模型在 backend、根plugins/models.py、Dropbox 中各自漂移重复如今收敛为单一实现。三、omi-*-app/28 个可独立部署的 FastAPI 插件服务3.1 自包含服务的组织原则每个omi-name-app/目录notion、github、slack、dropbox、whoop……都是一个自包含的 FastAPI 服务拥有自己的main.py、依赖文件与部署描述符Dockerfile / Procfile / railway.toml彼此独立部署、也与遗留单体相互独立。README 记载这类服务共 28 个具体数量以仓库当前目录清单为准。以 plugins/omi-notion-app/ 为例其目录构成展示了典型形态omi-notion-app/ ├── Procfile # Heroku 风格进程声明 ├── README.md ├── db.py # 业务数据存取 ├── main.py # FastAPI 应用入口 ├── models.py ├── notion_content.py # Notion 内容拼装 ├── railway.toml # Railway 部署描述符 ├── requirements.txt # 独立依赖不含 SDK ├── test_main.py # 应用级测试 └── test_pagination.py关键点在于业务逻辑OAuth 状态、持久化设置、提供商客户端全部留在 app 内部SDK 只提供 webhook 载荷模型——这是 plugins/omi-plugin-sdk/README.md 明确划定的边界App-specific OAuth state, persisted settings, provider clients, and business logic stay inside each app.3.2 部署描述符以 Notion 插件为例plugins/omi-notion-app/railway.toml 展示了 Nixpacks 构建方式的配置[build] builder nixpacks [deploy] startCommand uvicorn main:app --host 0.0.0.0 --port ${PORT:-8080} healthcheckPath /health healthcheckTimeout 100 restartPolicyType on_failure restartPolicyMaxRetries 3值得注意的细节启动命令读取PORT环境变量并默认 8080与遗留单体 Dockerfile 的EXPOSE 8080端口约定一致健康检查路径为/health超时 100 秒失败重启策略为on_failure、最多 3 次重试该服务的requirements.txtplugins/omi-notion-app/requirements.txt是独立于 monolith 的依赖集fastapi、uvicorn、python-dotenv、requests、pydantic、Jinja2、python-multipart、redis 等并不包含./omi-plugin-sdk——因为 Notion 服务未导入 SDK 模型正如审计文档所列未添加 SDK 依赖除非 app 导入了 SDK 支撑的模型。3.3 使用 SDK 的独立服务与隔离构建风险与 Notion 不同dropbox、linear、hive、shopify、shipbob 等服务在requirements.txt中以../omi-plugin-sdk安装 SDK。审计文档 plugins/PLUGIN_REFACTOR_AUDIT.md 明确了两类事实迁移成果omi-dropbox-app已迁移为通过 SDK 的Conversation解析 webhook此前其本地ActionItem/Structured与后端漂移linear / hive / shopify / shipbob 的未来用 Omi webhook 模型均直接 re-export SDK业务模型保持本地构建约束../omi-plugin-sdk只在仓库 checkout 且 SDK 与 app 为同级目录的构建模式下有效。若某服务被配置为隔离根目录排除同级目录依赖安装将失败——这是评估任何拆出独立服务方案时必须先验证的前提。3.4 契约检查脚本check_plugin_imports.pyplugins/scripts/check_plugin_imports.py 是对隔离根构建模式的防护性检查工具。它遍历所有plugins/omi-*-app/main.py逐个执行把 app 目录与 SDK 的src目录临时加入sys.pathos.chdir到 app 目录importlib.import_module(main)导入服务要求模块暴露app属性调用app.openapi()生成 OpenAPI 模式验证 FastAPI 应用可正常初始化每次导入前通过_purge_plugin_modules清理上一次导入的插件模块缓存避免跨目录模块串扰。脚本最终打印checkedN并汇总 blockers缺失依赖、无app属性、导入异常等任一 blocker 存在即返回退出码 1。这个脚本本质上是在构建期之前做一次每个 app 能否在自身目录内独立解析并启动的冒烟验证。四、遗留单体main.pyDockerfile4.1 路由构成与启动方式plugins/main.py 是旧的一站式插件 API通过uvicorn main:app启动默认监听0.0.0.0:8080。从源码可见其挂载的 live routers路由模块说明basic/conversation_created核心会话 webhookconversation_created、mentor 等oauth/OAuth 流程相关 webhookzapier/Zapier 集成 webhookchatgpt/ChatGPT 集成subscription/订阅相关接口notifications/hey_omi通知触发iq_rating/IQ 评分_multion/最后一个遗留集成处于休眠状态main.py中还保留了被注释掉的 REALTIME 插件注册代码并附有一段有价值的弃用说明实时类插件因每 3 秒跑一次 LLM 逻辑、每天运行 10 小时成本过高、触发方式低效、缺少杀手级用例等原因被搁置。根路由/返回可用路由清单/score/、/subscription/、/chatgpt/、/docs静态资源通过app.mount(/templates/static, ...)挂载自templates/目录——该目录存放单体提供的设置流程 HTML见 plugins/templates/。4.2 Dockerfile 与依赖装配plugins/Dockerfile 采用两阶段构建FROM gcr.io/based-hardware-dev/python:3.11-slim-forky AS builder COPY plugins/requirements.txt /tmp/requirements.txt COPY plugins/omi-plugin-sdk /app/omi-plugin-sdk WORKDIR /app RUN pip install --no-cache-dir -r /tmp/requirements.txt FROM gcr.io/based-hardware-dev/python:3.11-slim-forky COPY plugins/ . EXPOSE 8080 CMD [uvicorn, main:app, --host, 0.0.0.0, --port, 8080]其中COPY plugins/omi-plugin-sdk /app/omi-plugin-sdk这一行正是为了让requirements.txt中的./omi-plugin-sdk路径依赖在镜像内可解析——构建期先把 SDK 复制进镜像再整体安装依赖。运行时层还安装了ffmpeg curl unzip等系统包针对 #7136 的安全补丁。4.3 保留原因与维护红线单体保留的唯一原因是部署目标仍存在plugins/LEGACY_MONOLITH.md 明确记载.github/workflows/gcp_plugins.yml仍会构建plugins/Dockerfile并以uvicorn main:app从plugins/包根启动只要该 Cloud Run 目标未被替换或移除删除单体会导致部署链路断裂。LEGACY_MONOLITH.md 同时给出了清晰的维护规则不要向单体添加新的插件业务逻辑新功能一律走独立omi-*-app服务保持根 plugins/models.py 作为omi_plugin_sdk.models的兼容层它 re-export SDK 模型并额外保留单体专用的实时/主动通知模型如RealtimePluginRequest、ProactiveNotificationResponse及其questionfilterspeople / entities / topics上下文查询结构不要删除_multion除非先替换或移除 GCP 插件部署目标保持plugins/Dockerfile与plugins/Dockerfile.datadog与单体决策对齐。五、其他目录兼容层、指令内容与独立托管服务models.py根级兼容层如 4.3 所述re-exportomi_plugin_sdk.models的全部模型并新增主动通知proactive notification响应模型。SDK 与兼容层并存使旧 import 路径无需改动即可继续工作scripts/check_plugin_imports.py即上文 3.4 的契约检查脚本instructions/每个 app 的指令/资产内容由移动端应用读取展示hume-ai/、composio/、uber_call/托管在 monorepo 部署工作流之外的独立服务logos/、import/静态资产与导入工具.env.template单体所需环境变量清单README 提及用于本地配置。六、历史决策与迁移审计为什么是现在这个形态plugins/PLUGIN_REFACTOR_AUDIT.md 是理解当前形态的关键决策记录2026-06-29单一模型实现Structured、ActionItem、Event曾分散在后端、根plugins/models.py、Dropbox 等多个位置并发生漂移重构后统一收敛到omi-plugin-sdk/src/omi_plugin_sdk/models.py后端与插件文件改为 re-export后端镜像的兼容回退由于backend/Dockerfile只复制backend/目录不含 SDKbackend/models/structured.py 在 SDK 不可用时保留后端本地的兼容实现完整仓库运行时才导入 SDK 版本逐步迁移而非破坏性重构已迁移服务dropbox 等用../omi-plugin-sdk相对路径安装未验证隔离根构建可行性前不做破坏性删除。七、总结Friend 的plugins/目录呈现了一条清晰的演进路线从单一遗留单体走向共享模型契约 独立微服务。omi-plugin-sdk用最小的 Pydantic 依赖承载了 webhook 载荷的全部字段语义会话、转写片段、行动项、结构化笔记、外部集成入参omi-*-app各自独立打包部署并只通过../omi-plugin-sdk复用模型而遗留单体则以LEGACY_MONOLITH.md的红线被冻结。对于插件开发者而言正确的实践是业务逻辑放进独立 appwebhook 模型统一 importomi_plugin_sdk.models并通过check_plugin_imports.py验证服务的隔离构建可行性。这一套体系同时服务于 AI 转写处理、OAuth 集成、通知与主动提醒等场景是理解 Friend 开放生态的入口。【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表