fix: assistant IA toujours en mode agent, retrait du bouton toggle #187
- Bouton « mode agent » du panneau supprimé : l'assistant est toujours agent (toute requête texte part sur /api/ai/bookslm/agent, les images restent sur /chat multimodal). Les mutations gardent la confirmation two-step. Deep Research et les quick actions `agent: true` basculaient déjà le mode en silence : le toggle ne protégeait plus rien. - /agent résout le provider comme /chat (req.provider brut transmis à l'adapter pouvait désigner un fournisseur indisponible et retomber silencieusement sur un autre que l'étiquette SSE affichée). - Schémas des outils mis en cache par (scope, taille du registre) avec copies fraîches par appelant (~27 ms de pydantic économisées par requête agent/MCP). - Aide in-app réécrite, i18n FR/EN (retrait de ai.agent_mode_*), tests frontend adaptés (ai.test.mjs 100/100) + non-régression pytest (provider résolu, cache sûr).
This commit is contained in:
+17
-12
@@ -536,18 +536,6 @@ async def api_bookslm_agent(
|
||||
# run no longer pauses on every subsequent mutating call.
|
||||
ctx.confirmed = True
|
||||
|
||||
async def _llm(msgs, tool_schemas):
|
||||
return await chat_completion(
|
||||
msgs,
|
||||
tools=tool_schemas,
|
||||
provider=req.provider,
|
||||
model=req.model,
|
||||
temperature=0.3,
|
||||
# Tool-call arguments can carry a whole file body (e.g. a generated
|
||||
# table): leave more room than the plain-chat default.
|
||||
max_tokens=8192,
|
||||
)
|
||||
|
||||
async def generate_sse():
|
||||
import asyncio
|
||||
|
||||
@@ -561,6 +549,23 @@ async def api_bookslm_agent(
|
||||
yield f"event: error\ndata: {error_data}\n\n"
|
||||
return
|
||||
|
||||
# #187: resolve the provider like /chat does — the agent must use
|
||||
# the same engine the SSE "provider" tag reports (the raw
|
||||
# req.provider could name an unavailable provider and silently
|
||||
# fall back to another one via _get_provider_config).
|
||||
async def _llm(msgs, tool_schemas):
|
||||
return await chat_completion(
|
||||
msgs,
|
||||
tools=tool_schemas,
|
||||
provider=cfg_name,
|
||||
model=req.model,
|
||||
temperature=0.3,
|
||||
# Tool-call arguments can carry a whole file body (e.g. a
|
||||
# generated table): leave more room than the plain-chat
|
||||
# default.
|
||||
max_tokens=8192,
|
||||
)
|
||||
|
||||
# Stream tool events live: each executed step is pushed on the
|
||||
# queue by the loop callback and emitted as soon as it happens,
|
||||
# so the UI can grow its « N steps » block while thinking.
|
||||
|
||||
@@ -117,9 +117,22 @@ def list_tools(*, scope: ToolScope | None = None) -> list[ToolSpec]:
|
||||
return specs
|
||||
|
||||
|
||||
# ponytail: tool schemas are static after import (registration is decorator
|
||||
# only); keying the cache on len(_REGISTRY) invalidates it if a tool is ever
|
||||
# registered at runtime. Rebuilding 50 pydantic JSON schemas cost ~27 ms per
|
||||
# agent/MCP request.
|
||||
_SCHEMAS_CACHE: dict[Any, list[dict[str, Any]]] = {}
|
||||
|
||||
|
||||
def get_tool_schemas(*, scope: ToolScope | None = None) -> list[dict[str, Any]]:
|
||||
"""Return OpenAI-compatible schemas for registered tools."""
|
||||
return [spec.openai_schema() for spec in list_tools(scope=scope)]
|
||||
"""Return OpenAI-compatible schemas for registered tools (cached)."""
|
||||
key = (scope, len(_REGISTRY))
|
||||
cached = _SCHEMAS_CACHE.get(key)
|
||||
if cached is None:
|
||||
cached = [spec.openai_schema() for spec in list_tools(scope=scope)]
|
||||
_SCHEMAS_CACHE.clear()
|
||||
_SCHEMAS_CACHE[key] = cached
|
||||
return [dict(s) for s in cached]
|
||||
|
||||
|
||||
def _audit(ctx: ToolContext, spec: ToolSpec, arguments: dict[str, Any], *, ok: bool, error: str | None = None) -> None:
|
||||
|
||||
Reference in New Issue
Block a user