mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-28 20:36:42 +08:00
fix(assistant): align resource tools with application services
This commit is contained in:
@@ -253,7 +253,8 @@ class AssistantService:
|
|||||||
await self._save(context, conversation, 'running')
|
await self._save(context, conversation, 'running')
|
||||||
try:
|
try:
|
||||||
result = await execute_tool(self.ap, context, func['name'], json.loads(func['arguments'] or '{}'))
|
result = await execute_tool(self.ap, context, func['name'], json.loads(func['arguments'] or '{}'))
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
|
self.ap.logger.warning('Management assistant tool %s failed (%s)', func['name'], type(exc).__name__)
|
||||||
self._append_result(
|
self._append_result(
|
||||||
conversation,
|
conversation,
|
||||||
call,
|
call,
|
||||||
|
|||||||
@@ -89,13 +89,18 @@ async def execute_tool(ap, context, name: str, arguments: dict):
|
|||||||
args = validate_call(context, name, arguments).model_dump(mode='json')
|
args = validate_call(context, name, arguments).model_dump(mode='json')
|
||||||
if name == 'list_resources':
|
if name == 'list_resources':
|
||||||
readers = {
|
readers = {
|
||||||
'models': ap.model_service.get_llm_models,
|
'models': ap.llm_model_service.get_llm_models,
|
||||||
'embedding_models': ap.model_service.get_embedding_models,
|
'embedding_models': ap.embedding_models_service.get_embedding_models,
|
||||||
'pipelines': ap.pipeline_service.get_pipelines,
|
'pipelines': ap.pipeline_service.get_pipelines,
|
||||||
'knowledge_bases': ap.knowledge_service.get_knowledge_bases,
|
'knowledge_bases': ap.knowledge_service.get_knowledge_bases,
|
||||||
'knowledge_engines': ap.knowledge_service.list_knowledge_engines,
|
'knowledge_engines': ap.knowledge_service.list_knowledge_engines,
|
||||||
}
|
}
|
||||||
return redact_secrets(await readers[args['kind']](context))
|
resources = await readers[args['kind']](context)
|
||||||
|
if args['kind'] != 'knowledge_engines':
|
||||||
|
# Lists discover resources; get_pipeline / get_knowledge_schema supply configuration details.
|
||||||
|
fields = ('uuid', 'name', 'description', 'abilities', 'knowledge_engine_plugin_id')
|
||||||
|
resources = [{key: item[key] for key in fields if key in item} for item in resources]
|
||||||
|
return {'total': len(resources), 'items': redact_secrets(resources)}
|
||||||
if name == 'get_pipeline':
|
if name == 'get_pipeline':
|
||||||
return await ap.pipeline_service.get_pipeline(context, args['pipeline_uuid'])
|
return await ap.pipeline_service.get_pipeline(context, args['pipeline_uuid'])
|
||||||
if name == 'get_knowledge_schema':
|
if name == 'get_knowledge_schema':
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock, create_autospec
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
@@ -13,7 +13,7 @@ from langbot_plugin.api.entities.builtin.provider.message import Message
|
|||||||
from langbot.pkg.api.http.authz import Permission
|
from langbot.pkg.api.http.authz import Permission
|
||||||
from langbot.pkg.api.http.context import RequestContext, PrincipalContext, PrincipalType, WorkspaceContext
|
from langbot.pkg.api.http.context import RequestContext, PrincipalContext, PrincipalType, WorkspaceContext
|
||||||
from langbot.pkg.api.http.service.assistant import AssistantError, AssistantService
|
from langbot.pkg.api.http.service.assistant import AssistantError, AssistantService
|
||||||
from langbot.pkg.api.http.service.assistant_tools import validate_call
|
from langbot.pkg.api.http.service.assistant_tools import execute_tool, validate_call
|
||||||
from langbot.pkg.entity.persistence.assistant import AssistantConversation
|
from langbot.pkg.entity.persistence.assistant import AssistantConversation
|
||||||
|
|
||||||
|
|
||||||
@@ -145,3 +145,33 @@ def test_tool_arguments_cannot_select_identity_or_shell():
|
|||||||
validate_call(context(), 'create_pipeline', {'name': 'test', 'workspace_uuid': 'other'})
|
validate_call(context(), 'create_pipeline', {'name': 'test', 'workspace_uuid': 'other'})
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
validate_call(context(), 'exec', {'command': 'echo unsafe'})
|
validate_call(context(), 'exec', {'command': 'echo unsafe'})
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_resource_readers_match_application_services():
|
||||||
|
from langbot.pkg.core.app import Application
|
||||||
|
from langbot.pkg.api.http.service.model import LLMModelsService, EmbeddingModelsService
|
||||||
|
from langbot.pkg.api.http.service.pipeline import PipelineService
|
||||||
|
from langbot.pkg.api.http.service.knowledge import KnowledgeService
|
||||||
|
|
||||||
|
ap = create_autospec(Application, instance=True, spec_set=True)
|
||||||
|
ap.llm_model_service = create_autospec(LLMModelsService, instance=True)
|
||||||
|
ap.embedding_models_service = create_autospec(EmbeddingModelsService, instance=True)
|
||||||
|
ap.pipeline_service = create_autospec(PipelineService, instance=True)
|
||||||
|
ap.knowledge_service = create_autospec(KnowledgeService, instance=True)
|
||||||
|
ctx = context()
|
||||||
|
for kind, reader in (
|
||||||
|
('models', ap.llm_model_service.get_llm_models),
|
||||||
|
('embedding_models', ap.embedding_models_service.get_embedding_models),
|
||||||
|
('pipelines', ap.pipeline_service.get_pipelines),
|
||||||
|
('knowledge_bases', ap.knowledge_service.get_knowledge_bases),
|
||||||
|
('knowledge_engines', ap.knowledge_service.list_knowledge_engines),
|
||||||
|
):
|
||||||
|
reader.return_value = [{'name': kind}]
|
||||||
|
if kind != 'knowledge_engines':
|
||||||
|
reader.return_value[0]['config'] = {'large_or_private': 'omitted from discovery'}
|
||||||
|
assert await execute_tool(ap, ctx, 'list_resources', {'kind': kind}) == {
|
||||||
|
'total': 1,
|
||||||
|
'items': [{'name': kind}],
|
||||||
|
}
|
||||||
|
reader.assert_awaited_once_with(ctx)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from 'react';
|
|||||||
import { MessageCircle, Plus, Send, X, LoaderCircle } from 'lucide-react';
|
import { MessageCircle, Plus, Send, X, LoaderCircle } from 'lucide-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
import { backendClient, useCurrentWorkspace, userInfo } from '@/app/infra/http';
|
import { backendClient, useCurrentWorkspace, userInfo } from '@/app/infra/http';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
@@ -230,6 +231,7 @@ function AssistantPanel({ storageKey }: { storageKey: string }) {
|
|||||||
className={`rounded-xl p-3 text-sm break-words ${message.role === 'user' ? 'ml-6 bg-primary/10' : 'bg-muted'}`}
|
className={`rounded-xl p-3 text-sm break-words ${message.role === 'user' ? 'ml-6 bg-primary/10' : 'bg-muted'}`}
|
||||||
>
|
>
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[remarkGfm]}
|
||||||
components={{
|
components={{
|
||||||
img: () => null,
|
img: () => null,
|
||||||
a: ({ href, children }) => (
|
a: ({ href, children }) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user