add first commit

This commit is contained in:
2026-07-21 15:43:39 +08:00
commit 482f8df508
19 changed files with 521 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
# Python-generated files
__pycache__/
*.py[oc]
build/
build_nuitka/
dist/
wheels/
*.egg-info
# Virtual environments
.venv
View File
+39
View File
@@ -0,0 +1,39 @@
"""
豆包视觉理解 Worker 插件
"""
import os
from volcenginesdkarkruntime import AsyncArk
from chongming_worker import create_workers as _create_workers
from chongming_worker import auto_bind_models
from .global_var import set_ark_client, cleanup_ark_client
from .router import *
# 自动绑定 gen_models 生成的 Pydantic 模型
auto_bind_models("doubao")
async def create_workers(session, config, count=1, worker_manager=None):
"""
工厂函数:由 app 生命周期调用,创建 Worker 协程。
"""
# 从配置或环境变量获取 API Key
api_key = config.get("api_key") or os.environ.get("VOLC_ACCESSKEY")
if not api_key:
raise ValueError("缺少 API_KEY,请在配置中设置 api_key 或环境变量 VOLC_ACCESSKEY")
base_url = config.get("base_url", "https://ark.cn-beijing.volces.com/api/v3")
model = config.get("model", "doubao-seed-evolving")
# 将模型名存入环境变量,供 router 使用
os.environ["DOUBAO_MODEL"] = model
# 创建全局客户端
client = AsyncArk(base_url=base_url, api_key=api_key)
set_ark_client(client)
# 委托给通用运行时创建 Worker 任务
return _create_workers(session, config, count, worker_manager)
async def cleanup(worker_item: str):
"""清理资源"""
cleanup_ark_client()
print(f"{worker_item} 清理完毕")
+17
View File
@@ -0,0 +1,17 @@
from typing import Optional
from volcenginesdkarkruntime import AsyncArk
_ark_client: Optional[AsyncArk] = None
def set_ark_client(client: AsyncArk):
global _ark_client
_ark_client = client
def get_ark_client() -> Optional[AsyncArk]:
return _ark_client
def cleanup_ark_client():
global _ark_client
if _ark_client:
# AsyncArk 无显式 close,但可设为 None
_ark_client = None
+14
View File
@@ -0,0 +1,14 @@
"""
模型导出 — doubao
自动生成,请勿手动修改
"""
from .pydantic import AnalyzePostRequest
from .pydantic import AnalyzePostResponse
from .pydantic import DoubaoResponse
__all__ = [
"AnalyzePostRequest",
"AnalyzePostResponse",
"DoubaoResponse",
]
+23
View File
@@ -0,0 +1,23 @@
"""
自动生成的 Pydantic 模型 — doubao
来源: config/workers/doubao/
"""
from __future__ import annotations
from typing import Any, Optional
from pydantic import BaseModel, Field
class AnalyzePostRequest(BaseModel):
"""分析图片内容 — 请求体"""
image_base64: str = Field(...)
prompt: str = Field(...)
class AnalyzePostResponse(BaseModel):
"""分析图片内容 — 响应体"""
result: str = Field(...)
error: Optional[str] = Field(default='')
# 所有响应模型的联合类型
DoubaoResponse = AnalyzePostResponse
+3
View File
@@ -0,0 +1,3 @@
from .analyze import analyze
__all__ = ["analyze"]
+48
View File
@@ -0,0 +1,48 @@
import os
from chongming_worker import rpc_method
from ..models.pydantic import AnalyzePostRequest, AnalyzePostResponse
from ..global_var import get_ark_client
@rpc_method(
subject="doubao.analyze",
method="POST",
summary="Analyze image via Doubao vision model",
response_model=AnalyzePostResponse,
)
async def analyze(request: AnalyzePostRequest) -> AnalyzePostResponse:
print(f"[DEBUG] analyze called with prompt={request.prompt}, image_base64 length={len(request.image_base64)}")
client = get_ark_client()
if client is None:
return AnalyzePostResponse(result="", error="客户端未初始化")
# 获取图片数据
image_base64 = request.image_base64
if not image_base64:
return AnalyzePostResponse(result="", error="未提供图片数据(image_base64或image_path)")
if image_base64.startswith('data:image/'):
image_base64 = image_base64.split(',', 1)[1]
mime_type = "image/png"
prompt = request.prompt or ""
try:
response = await client.chat.completions.create(
model=os.getenv("DOUBAO_MODEL", "doubao-seed-evolving"),
messages=[
{
"role": "user",
"content": [
{"type": "image_url", "image_url": f"data:{mime_type};base64,{image_base64}"},
{"type": "text", "text": prompt}
]
} # type: ignore
],
thinking={"type": "disabled"}
)
result = response.choices[0].message.content # type: ignore
return AnalyzePostResponse(result=result) # type: ignore
except Exception as e:
return AnalyzePostResponse(result="", error=f"调用豆包API失败: {str(e)}")
+50
View File
@@ -0,0 +1,50 @@
"""
Example Worker 插件 — 演示 @rpc_method + 显式模型绑定用法
本插件展示如何使用 chongming_worker 框架定义 RPC 端点。
核心工作流:
1. 编写 config/workers/example/*.toml 配置文件
2. 执行 uv run gen_models 生成 Pydantic 模型
3. 在 __init__.py 中用 @rpc_method 定义业务函数
4. 显式导入模型并绑定到对应端点(替代 auto_bind_models)
关键约定:
- 模型命名: <Operation><Method>{Request|Response}
(如 AddGetRequest, AddGetResponse 由 gen_models 自动生成)
- 装饰器 subject 中的操作名 → 模型前缀(驼峰)
example.add → Add, example.subtract → Subtract
"""
import modelvault
from pathlib import Path
from chongming_worker import auto_bind_models
from chongming_worker.runtime import create_workers as _create_workers
from .router import *
auto_bind_models("example")
# ============================================================
# 工厂函数 — 供 app lifespan 动态加载
# ============================================================
# 框架通过 __import__ + getattr 获取此函数,
# 然后调用 create_workers(session, config, count, worker_manager=worker_manager)。
# 详见 src/chongming/app/__init__.py 的 lifespan。
async def create_workers(session, config, count=1, worker_manager=None):
"""插件入口 — 委托给 chongming_worker 运行时创建 Worker 协程"""
from .global_var import set_global_var
db_path = Path(config.get("data_dir", "./data")) / "example.modelvault"
db_path.parent.mkdir(parents=True, exist_ok=True)
db = await modelvault.AsyncDatabase.open(str(db_path)) # type: ignore
set_global_var("db", db)
return _create_workers(session, config, count, worker_manager)
async def cleanup(worker_item: str):
print(f"{worker_item}关闭")
pass
+24
View File
@@ -0,0 +1,24 @@
import sys
from typing import Any, Callable, Awaitable, List
import modelvault
# 初始定义(可以保留 db 作为示例)
db: modelvault.AsyncDatabase | None = None # type: ignore
def set_global_var(name: str, value: Any) -> None:
"""
设置当前模块的全局变量。
Args:
name: 变量名(字符串)
value: 任意值
"""
globals()[name] = value
# 但这样会污染导出列表,不推荐自动添加。
__all__ = [
"db",
"set_global_var",
]
+32
View File
@@ -0,0 +1,32 @@
"""
模型导出 — example
自动生成,请勿手动修改
"""
from .pydantic import AddGetRequest
from .pydantic import AddGetResponse
from .pydantic import AddPostRequest
from .pydantic import AddPostResponse
from .pydantic import CountGetResponse
from .pydantic import DivideGetRequest
from .pydantic import DivideGetResponse
from .pydantic import ExampleResponse
from .pydantic import MultiplyGetRequest
from .pydantic import MultiplyGetResponse
from .pydantic import SubtractGetRequest
from .pydantic import SubtractGetResponse
__all__ = [
"AddGetRequest",
"AddGetResponse",
"AddPostRequest",
"AddPostResponse",
"CountGetResponse",
"DivideGetRequest",
"DivideGetResponse",
"ExampleResponse",
"MultiplyGetRequest",
"MultiplyGetResponse",
"SubtractGetRequest",
"SubtractGetResponse",
]
+14
View File
@@ -0,0 +1,14 @@
# src/plugins/example/models/counter.py
from pydantic import BaseModel
import modelvault
class Counter(BaseModel):
# 主键字段
__modelvault_primary_key__ = "name"
# 唯一索引
__modelvault_indexes__ = [
modelvault.models.unique("name"),
]
name: str # 计数器名称(例如 "visits")
value: int = 0
+74
View File
@@ -0,0 +1,74 @@
"""
自动生成的 Pydantic 模型 — example
来源: config/workers/example/
"""
from __future__ import annotations
from typing import Any, Optional
from pydantic import BaseModel, Field
class AddGetRequest(BaseModel):
"""Add two numbers — 请求体"""
a: float = Field(...)
b: float = Field(...)
class AddGetResponse(BaseModel):
"""Add two numbers — 响应体"""
result: float = Field(...)
operation: str = Field(default='add')
timestamp: Optional[float] = Field(default=0.0)
class AddPostRequest(BaseModel):
"""Add two numbers (POST) — 请求体"""
a: float = Field(...)
b: float = Field(...)
class AddPostResponse(BaseModel):
"""Add two numbers (POST) — 响应体"""
result: float = Field(...)
operation: str = Field(default='add')
timestamp: Optional[float] = Field(default=0.0)
class CountGetResponse(BaseModel):
"""Get visit count and increment — 响应体"""
name: str = Field(...)
value: int = Field(...)
class DivideGetRequest(BaseModel):
"""Divide two numbers — 请求体"""
a: float = Field(...)
b: float = Field(...)
class DivideGetResponse(BaseModel):
"""Divide two numbers — 响应体"""
result: Optional[float] = Field(default=0.0)
operation: str = Field(default='divide')
timestamp: Optional[float] = Field(default=0.0)
error: Optional[str] = Field(default='')
class MultiplyGetRequest(BaseModel):
"""Multiply two numbers — 请求体"""
a: float = Field(...)
b: float = Field(...)
class MultiplyGetResponse(BaseModel):
"""Multiply two numbers — 响应体"""
result: float = Field(...)
operation: str = Field(default='multiply')
timestamp: Optional[float] = Field(default=0.0)
class SubtractGetRequest(BaseModel):
"""Subtract two numbers — 请求体"""
a: float = Field(...)
b: float = Field(...)
class SubtractGetResponse(BaseModel):
"""Subtract two numbers — 响应体"""
result: float = Field(...)
operation: str = Field(default='subtract')
timestamp: Optional[float] = Field(default=0.0)
# 所有响应模型的联合类型
ExampleResponse = SubtractGetResponse | CountGetResponse | MultiplyGetResponse | AddPostResponse | AddGetResponse | DivideGetResponse
+14
View File
@@ -0,0 +1,14 @@
from .add import add, add_post
from .subtract import subtract
from .multiply import multiply
from .divide import divide
from .count import count_get
__all__ = [
"add",
"add_post",
"subtract",
"multiply",
"divide",
"count_get"
]
+52
View File
@@ -0,0 +1,52 @@
import time
import zenoh
from chongming_worker import rpc_method
from chongming_request import Client
from ..models.pydantic import (
AddGetRequest,
AddGetResponse,
AddPostRequest,
AddPostResponse
)
@rpc_method(
subject="example.add",
method="GET",
summary="Add two numbers via GET",
response_model=AddGetResponse,
)
async def add(add_get_request:AddGetRequest) -> AddGetResponse:
"""加法运算(GET)— 通过查询参数传入 a、b,返回它们的和"""
return AddGetResponse(
result=add_get_request.a + add_get_request.b,
operation="add",
timestamp=time.time()
)
@rpc_method(
subject="example.add",
method="POST",
summary="Add two numbers via POST",
response_model=AddPostResponse,
)
async def add_post(add_post_request: AddPostRequest, session: zenoh.Session) -> AddPostResponse:
"""加法运算(POST)— 通过 JSON Body 传入 a、b,返回它们的和"""
async with Client(session=session) as client:
add_result = await client.call(
worker_type="example",
subject="example.add",
payload={
"a":add_post_request.a,
"b":add_post_request.b
},
method="GET"
)
print(add_result)
return AddPostResponse(
result=add_post_request.a + add_post_request.b,
operation="add",
timestamp=time.time(),
)
+33
View File
@@ -0,0 +1,33 @@
import modelvault
from chongming_worker import rpc_method
from ..models.pydantic import CountGetResponse
from ..models.counter import Counter
@rpc_method(
subject="example.count",
method="GET",
summary="Increment and get counter value",
response_model=CountGetResponse, # 需要定义 CountResponse
)
async def count_get() -> CountGetResponse:
from ..global_var import db
# 确保数据库已初始化(如果未初始化,则同步初始化,但这里假设已就绪)
if db is None:
raise RuntimeError("Database not initialized")
counters = modelvault.models.async_collection(db, Counter) # type: ignore
async with db.transaction():
counter = await counters.get("visits")
if counter is None:
counter = Counter(name="visits", value=0)
await counters.insert(counter)
else:
new_value = counter.value + 1
await counters.update("visits", {"value": new_value})
counter.value = new_value # 更新本地对象以便返回
return CountGetResponse(name=counter.name, value=counter.value) # type: ignore
+29
View File
@@ -0,0 +1,29 @@
import time
from chongming_worker import rpc_method
from ..models.pydantic import (
DivideGetResponse,
DivideGetRequest,
)
@rpc_method(
subject="example.divide",
method="GET",
summary="Divide two numbers",
response_model=DivideGetResponse,
)
async def divide(divide_get_request: DivideGetRequest) -> DivideGetResponse:
"""除法运算:a / b(b 不能为 0)"""
if divide_get_request.b == 0:
return DivideGetResponse(
error="除数不能为零",
operation="divide",
result=0.0,
timestamp=time.time(),
)
return DivideGetResponse(
result=divide_get_request.a / divide_get_request.b,
operation="divide",
timestamp=time.time(),
)
+22
View File
@@ -0,0 +1,22 @@
import time
from chongming_worker import rpc_method
from ..models.pydantic import (
MultiplyGetRequest,
MultiplyGetResponse,
)
@rpc_method(
subject="example.multiply",
method="GET",
summary="Multiply two numbers",
response_model=MultiplyGetResponse,
)
async def multiply(multiply_get_request=MultiplyGetRequest) -> MultiplyGetResponse:
"""乘法运算:a * b"""
return MultiplyGetResponse(
result=multiply_get_request.a * multiply_get_request.b,
operation="multiply",
timestamp=time.time(),
)
+22
View File
@@ -0,0 +1,22 @@
import time
from chongming_worker import rpc_method
from ..models.pydantic import (
SubtractGetResponse,
SubtractGetRequest,
)
@rpc_method(
subject="example.subtract",
method="GET",
summary="Subtract two numbers",
response_model=SubtractGetResponse,
)
async def subtract(subtract_get_request: SubtractGetRequest) -> SubtractGetResponse:
"""减法运算:a - b"""
return SubtractGetResponse(
result=subtract_get_request.a - subtract_get_request.b,
operation="subtract",
timestamp=time.time(),
)