add first commit
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
||||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[oc]
|
||||
build/
|
||||
build_nuitka/
|
||||
dist/
|
||||
wheels/
|
||||
*.egg-info
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
@@ -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} 清理完毕")
|
||||
@@ -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
|
||||
@@ -0,0 +1,14 @@
|
||||
"""
|
||||
模型导出 — doubao
|
||||
自动生成,请勿手动修改
|
||||
"""
|
||||
|
||||
from .pydantic import AnalyzePostRequest
|
||||
from .pydantic import AnalyzePostResponse
|
||||
from .pydantic import DoubaoResponse
|
||||
|
||||
__all__ = [
|
||||
"AnalyzePostRequest",
|
||||
"AnalyzePostResponse",
|
||||
"DoubaoResponse",
|
||||
]
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
from .analyze import analyze
|
||||
|
||||
__all__ = ["analyze"]
|
||||
@@ -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)}")
|
||||
@@ -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
|
||||
@@ -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",
|
||||
]
|
||||
@@ -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",
|
||||
]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
]
|
||||
@@ -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(),
|
||||
)
|
||||
@@ -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
|
||||
@@ -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(),
|
||||
)
|
||||
@@ -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(),
|
||||
)
|
||||
@@ -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(),
|
||||
)
|
||||
Reference in New Issue
Block a user