mimo-token-plan-adapter
CI / gate (push) Has been cancelled

This commit is contained in:
QihuAI Design committed 2026-07-26 21:47:48 +08:00
1 parent bb32884ecb
commit c624d8f37c
3 files changed
+27 -9

No files matched your search

+3
View File
@@ -3041,6 +3041,7 @@ async def _send_message_core(
api_key = ai_model.api_key or (provider.api_key if provider else None) api_key = ai_model.api_key or (provider.api_key if provider else None)
auth_header = provider.auth_header if provider and hasattr(provider, "auth_header") else None
@@ -4822,6 +4823,7 @@ async def _send_message_core(
proxy=_proxy_for_url(api_url), proxy=_proxy_for_url(api_url),
timeout=ai_model.timeout or 120, timeout=ai_model.timeout or 120,
stream=True, stream=True,
auth_header=auth_header,
): ):
if _kind == "content": if _kind == "content":
_sc += _text _sc += _text
@@ -4839,6 +4841,7 @@ async def _send_message_core(
request_body, request_body,
proxy=_proxy_for_url(api_url), proxy=_proxy_for_url(api_url),
timeout=ai_model.timeout or 120, timeout=ai_model.timeout or 120,
auth_header=auth_header,
) )
except Exception as _exc: except Exception as _exc:
+23 -9
View File
@@ -33,7 +33,7 @@ def _base_url(api_url: str) -> str:
return u return u
def build_openai_client(api_url, api_key, *, proxy=None, timeout=120) -> AsyncOpenAI: def build_openai_client(api_url, api_key, *, proxy=None, timeout=120, auth_header=None) -> AsyncOpenAI:
base = _base_url(api_url) base = _base_url(api_url)
# 始终 trust_env=False:避免容器 HTTP_PROXY 对国内端点(siliconflow 等)造成 502; # 始终 trust_env=False:避免容器 HTTP_PROXY 对国内端点(siliconflow 等)造成 502;
# 代理经 proxy 显式传入(AGNES 等),未传则直连。 # 代理经 proxy 显式传入(AGNES 等),未传则直连。
@@ -49,11 +49,19 @@ def build_openai_client(api_url, api_key, *, proxy=None, timeout=120) -> AsyncOp
write=min(30.0, _num), write=min(30.0, _num),
pool=10.0, pool=10.0,
) )
_hdrs = {}
if auth_header and ":" in auth_header:
_k, _v = auth_header.split(":", 1)
_hdrs[_k.strip()] = _v.strip()
elif auth_header:
_hdrs[auth_header.strip()] = api_key
return AsyncOpenAI( return AsyncOpenAI(
base_url=base, base_url=base,
api_key=api_key or "EMPTY", api_key=api_key or "EMPTY",
timeout=_to, timeout=_to,
http_client=http_client, http_client=http_client,
default_headers=_hdrs if _hdrs else None,
) )
@@ -96,12 +104,12 @@ def _normalize_tool_calls(tcs):
return out return out
async def achat_complete(api_url, api_key, request_body, *, proxy=None, timeout=120, stream=False): async def achat_complete(api_url, api_key, request_body, *, proxy=None, timeout=120, stream=False, auth_header=None):
"""统一的 OpenAI 兼容聊天补全。详见模块 docstring。""" """统一的 OpenAI 兼容聊天补全。详见模块 docstring。"""
if stream: if stream:
async def _gen(): async def _gen():
client = build_openai_client(api_url, api_key, proxy=proxy, timeout=timeout) client = build_openai_client(api_url, api_key, proxy=proxy, timeout=timeout, auth_header=auth_header)
_content_parts = [] _content_parts = []
_reasoning_parts = [] _reasoning_parts = []
_tool_acc = {} # index -> {id, type, name, arguments} _tool_acc = {} # index -> {id, type, name, arguments}
@@ -175,7 +183,7 @@ async def achat_complete(api_url, api_key, request_body, *, proxy=None, timeout=
return _gen() return _gen()
client = build_openai_client(api_url, api_key, proxy=proxy, timeout=timeout) client = build_openai_client(api_url, api_key, proxy=proxy, timeout=timeout, auth_header=auth_header)
try: try:
_to = min(30.0, float(timeout) if isinstance(timeout, (int, float)) else 120.0) _to = min(30.0, float(timeout) if isinstance(timeout, (int, float)) else 120.0)
resp = await asyncio.wait_for( resp = await asyncio.wait_for(
@@ -202,16 +210,22 @@ async def achat_complete(api_url, api_key, request_body, *, proxy=None, timeout=
pass pass
def build_openai_client_sync(api_url, api_key, *, proxy=None, timeout=120) -> OpenAI: def build_openai_client_sync(api_url, api_key, *, proxy=None, timeout=120, auth_header=None) -> OpenAI:
"""同步客户端工厂(供 IM bot 等同步上下文复用同一标准模型层)。""" """同步客户端工厂(供 IM bot 等同步上下文复用同一标准模型层)。"""
base = _base_url(api_url) base = _base_url(api_url)
http_client = httpx.Client(proxy=proxy, trust_env=False) http_client = httpx.Client(proxy=proxy, trust_env=False)
return OpenAI(base_url=base, api_key=api_key or "EMPTY", timeout=timeout, http_client=http_client) _hdrs = {}
if auth_header and ":" in auth_header:
_k, _v = auth_header.split(":", 1)
_hdrs[_k.strip()] = _v.strip()
elif auth_header:
_hdrs[auth_header.strip()] = api_key
return OpenAI(base_url=base, api_key=api_key or "EMPTY", timeout=timeout, http_client=http_client, default_headers=_hdrs if _hdrs else None)
def chat_complete(api_url, api_key, request_body, *, proxy=None, timeout=120): def chat_complete(api_url, api_key, request_body, *, proxy=None, timeout=120):
"""同步版(与 achat_complete 行为/返回形状完全一致),供非 async 上下文使用。""" """同步版(与 achat_complete 行为/返回形状完全一致),供非 async 上下文使用。"""
client = build_openai_client_sync(api_url, api_key, proxy=proxy, timeout=timeout) client = build_openai_client_sync(api_url, api_key, proxy=proxy, timeout=timeout, auth_header=auth_header)
with client: with client:
resp = client.chat.completions.create(**_create_kwargs(request_body)) resp = client.chat.completions.create(**_create_kwargs(request_body))
msg = resp.choices[0].message msg = resp.choices[0].message
@@ -242,7 +256,7 @@ async def aembed(api_url, api_key, input, model, *, proxy=None, timeout=60) -> l
供 pgvector_search.get_embedding 等 embeddings 路径复用,把散落的裸 httpx 供 pgvector_search.get_embedding 等 embeddings 路径复用,把散落的裸 httpx
嵌入调用收敛到同一标准模型层(与聊天共用代理/超时/failover 语义)。 嵌入调用收敛到同一标准模型层(与聊天共用代理/超时/failover 语义)。
""" """
client = build_openai_client(api_url, api_key, proxy=proxy, timeout=timeout) client = build_openai_client(api_url, api_key, proxy=proxy, timeout=timeout, auth_header=auth_header)
try: try:
_to = min(60.0, float(timeout) if isinstance(timeout, (int, float)) else 60.0) _to = min(60.0, float(timeout) if isinstance(timeout, (int, float)) else 60.0)
resp = await asyncio.wait_for( resp = await asyncio.wait_for(
@@ -259,7 +273,7 @@ async def aembed(api_url, api_key, input, model, *, proxy=None, timeout=60) -> l
def embed(api_url, api_key, input, model, *, proxy=None, timeout=60) -> list: def embed(api_url, api_key, input, model, *, proxy=None, timeout=60) -> list:
"""同步版嵌入(与非 async 上下文复用同一标准模型层)。""" """同步版嵌入(与非 async 上下文复用同一标准模型层)。"""
client = build_openai_client_sync(api_url, api_key, proxy=proxy, timeout=timeout) client = build_openai_client_sync(api_url, api_key, proxy=proxy, timeout=timeout, auth_header=auth_header)
with client: with client:
resp = client.embeddings.create(**_embed_kwargs(input, model)) resp = client.embeddings.create(**_embed_kwargs(input, model))
return [d.embedding for d in resp.data] return [d.embedding for d in resp.data]
+1
View File
@@ -530,6 +530,7 @@ class ModelProvider(Base):
protocol = Column(String(30), nullable=False, default="openai_compatible") protocol = Column(String(30), nullable=False, default="openai_compatible")
api_key = Column(String(255), default=None) api_key = Column(String(255), default=None)
api_base_url = Column(String(500), default=None) api_base_url = Column(String(500), default=None)
auth_header = Column(String(100), default="Authorization: Bearer", comment="自定义认证头,如MiMo用api-key")
created_at = Column(DateTime, default=datetime.now) created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)