v2.3.0 (#429)
Co-authored-by: Carlo <carloantonio.patti@mistral.ai> Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai> Co-authored-by: Clement Sirieix <clem.sirieix@gmail.com> Co-authored-by: Michel Thomazo <michel.thomazo@mistral.ai> Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Vincent Guilloux <vincent.guilloux@mistral.ai> Co-authored-by: Thomas Kenbeek <thomas.kenbeek@mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
a560a47ce8
commit
5d2e01a6d7
139 changed files with 7152 additions and 1457 deletions
|
|
@ -37,14 +37,12 @@ from acp.schema import (
|
|||
Implementation,
|
||||
ListSessionsResponse,
|
||||
McpServerStdio,
|
||||
ModelInfo,
|
||||
PromptCapabilities,
|
||||
ResumeSessionResponse,
|
||||
SessionCapabilities,
|
||||
SessionInfo,
|
||||
SessionListCapabilities,
|
||||
SessionModelState,
|
||||
SessionModeState,
|
||||
SetSessionConfigOptionResponse,
|
||||
SseMcpServer,
|
||||
TextContentBlock,
|
||||
TextResourceContents,
|
||||
|
|
@ -56,6 +54,7 @@ from acp.schema import (
|
|||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from vibe import VIBE_ROOT, __version__
|
||||
from vibe.acp.acp_logger import acp_message_observer
|
||||
from vibe.acp.tools.base import BaseAcpTool
|
||||
from vibe.acp.tools.session_update import (
|
||||
tool_call_session_update,
|
||||
|
|
@ -71,12 +70,13 @@ from vibe.acp.utils import (
|
|||
create_tool_call_replay,
|
||||
create_tool_result_replay,
|
||||
create_user_message_replay,
|
||||
get_all_acp_session_modes,
|
||||
get_proxy_help_text,
|
||||
is_valid_acp_agent,
|
||||
is_valid_acp_mode,
|
||||
make_mode_response,
|
||||
make_model_response,
|
||||
)
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
from vibe.core.agents.models import BuiltinAgentName
|
||||
from vibe.core.agents.models import CHAT as CHAT_AGENT, BuiltinAgentName
|
||||
from vibe.core.autocompletion.path_prompt_adapter import render_path_prompt
|
||||
from vibe.core.config import (
|
||||
MissingAPIKeyError,
|
||||
|
|
@ -98,6 +98,7 @@ from vibe.core.types import (
|
|||
AsyncApprovalCallback,
|
||||
CompactEndEvent,
|
||||
CompactStartEvent,
|
||||
EntrypointMetadata,
|
||||
LLMMessage,
|
||||
ReasoningEvent,
|
||||
Role,
|
||||
|
|
@ -121,7 +122,8 @@ class VibeAcpAgentLoop(AcpAgent):
|
|||
|
||||
def __init__(self) -> None:
|
||||
self.sessions: dict[str, AcpSessionLoop] = {}
|
||||
self.client_capabilities = None
|
||||
self.client_capabilities: ClientCapabilities | None = None
|
||||
self.client_info: Implementation | None = None
|
||||
|
||||
@override
|
||||
async def initialize(
|
||||
|
|
@ -132,6 +134,7 @@ class VibeAcpAgentLoop(AcpAgent):
|
|||
**kwargs: Any,
|
||||
) -> InitializeResponse:
|
||||
self.client_capabilities = client_capabilities
|
||||
self.client_info = client_info
|
||||
|
||||
# The ACP Agent process can be launched in 3 different ways, depending on installation
|
||||
# - dev mode: `uv run vibe-acp`, ran from the project root
|
||||
|
|
@ -200,6 +203,14 @@ class VibeAcpAgentLoop(AcpAgent):
|
|||
) -> AuthenticateResponse | None:
|
||||
raise NotImplementedError("Not implemented yet")
|
||||
|
||||
def _build_entrypoint_metadata(self) -> EntrypointMetadata:
|
||||
return EntrypointMetadata(
|
||||
agent_entrypoint="acp",
|
||||
agent_version=__version__,
|
||||
client_name=self.client_info.name if self.client_info else "",
|
||||
client_version=self.client_info.version if self.client_info else "",
|
||||
)
|
||||
|
||||
def _load_config(self) -> VibeConfig:
|
||||
try:
|
||||
config = VibeConfig.load(disabled_tools=["ask_user_question"])
|
||||
|
|
@ -223,21 +234,6 @@ class VibeAcpAgentLoop(AcpAgent):
|
|||
|
||||
return session
|
||||
|
||||
def _build_session_model_state(self, agent_loop: AgentLoop) -> SessionModelState:
|
||||
return SessionModelState(
|
||||
current_model_id=agent_loop.config.active_model,
|
||||
available_models=[
|
||||
ModelInfo(model_id=model.alias, name=model.alias)
|
||||
for model in agent_loop.config.models
|
||||
],
|
||||
)
|
||||
|
||||
def _build_session_mode_state(self, session: AcpSessionLoop) -> SessionModeState:
|
||||
return SessionModeState(
|
||||
current_mode_id=session.agent_loop.agent_profile.name,
|
||||
available_modes=get_all_acp_session_modes(session.agent_loop.agent_manager),
|
||||
)
|
||||
|
||||
@override
|
||||
async def new_session(
|
||||
self,
|
||||
|
|
@ -251,19 +247,32 @@ class VibeAcpAgentLoop(AcpAgent):
|
|||
config = self._load_config()
|
||||
|
||||
agent_loop = AgentLoop(
|
||||
config=config, agent_name=BuiltinAgentName.DEFAULT, enable_streaming=True
|
||||
config=config,
|
||||
agent_name=BuiltinAgentName.DEFAULT,
|
||||
enable_streaming=True,
|
||||
entrypoint_metadata=self._build_entrypoint_metadata(),
|
||||
)
|
||||
agent_loop.agent_manager.register_agent(CHAT_AGENT)
|
||||
# NOTE: For now, we pin session.id to agent_loop.session_id right after init time.
|
||||
# We should just use agent_loop.session_id everywhere, but it can still change during
|
||||
# session lifetime (e.g. agent_loop.compact is called).
|
||||
# We should refactor agent_loop.session_id to make it immutable in ACP context.
|
||||
session = await self._create_acp_session(agent_loop.session_id, agent_loop)
|
||||
agent_loop.emit_new_session_telemetry("acp")
|
||||
agent_loop.emit_new_session_telemetry()
|
||||
|
||||
modes_state, modes_config = make_mode_response(
|
||||
list(agent_loop.agent_manager.available_agents.values()),
|
||||
session.agent_loop.agent_profile.name,
|
||||
)
|
||||
models_state, models_config = make_model_response(
|
||||
agent_loop.config.models, agent_loop.config.active_model
|
||||
)
|
||||
|
||||
return NewSessionResponse(
|
||||
session_id=session.id,
|
||||
models=self._build_session_model_state(agent_loop),
|
||||
modes=self._build_session_mode_state(session),
|
||||
models=models_state,
|
||||
modes=modes_state,
|
||||
config_options=[modes_config, models_config],
|
||||
)
|
||||
|
||||
def _get_acp_tool_overrides(self) -> list[Path]:
|
||||
|
|
@ -447,8 +456,12 @@ class VibeAcpAgentLoop(AcpAgent):
|
|||
}) from e
|
||||
|
||||
agent_loop = AgentLoop(
|
||||
config=config, agent_name=BuiltinAgentName.DEFAULT, enable_streaming=True
|
||||
config=config,
|
||||
agent_name=BuiltinAgentName.DEFAULT,
|
||||
enable_streaming=True,
|
||||
entrypoint_metadata=self._build_entrypoint_metadata(),
|
||||
)
|
||||
agent_loop.agent_manager.register_agent(CHAT_AGENT)
|
||||
|
||||
non_system_messages = [
|
||||
msg for msg in loaded_messages if msg.role != Role.system
|
||||
|
|
@ -460,19 +473,24 @@ class VibeAcpAgentLoop(AcpAgent):
|
|||
|
||||
await self._replay_conversation_history(session_id, non_system_messages)
|
||||
|
||||
return LoadSessionResponse(
|
||||
models=self._build_session_model_state(agent_loop),
|
||||
modes=self._build_session_mode_state(session),
|
||||
modes_state, modes_config = make_mode_response(
|
||||
list(agent_loop.agent_manager.available_agents.values()),
|
||||
session.agent_loop.agent_profile.name,
|
||||
)
|
||||
models_state, models_config = make_model_response(
|
||||
agent_loop.config.models, agent_loop.config.active_model
|
||||
)
|
||||
|
||||
@override
|
||||
async def set_session_mode(
|
||||
self, mode_id: str, session_id: str, **kwargs: Any
|
||||
) -> SetSessionModeResponse | None:
|
||||
session = self._get_session(session_id)
|
||||
return LoadSessionResponse(
|
||||
models=models_state,
|
||||
modes=modes_state,
|
||||
config_options=[modes_config, models_config],
|
||||
)
|
||||
|
||||
if not is_valid_acp_agent(session.agent_loop.agent_manager, mode_id):
|
||||
return None
|
||||
async def _apply_mode_change(self, session: AcpSessionLoop, mode_id: str) -> bool:
|
||||
profiles = list(session.agent_loop.agent_manager.available_agents.values())
|
||||
if not is_valid_acp_mode(profiles, mode_id):
|
||||
return False
|
||||
|
||||
await session.agent_loop.switch_agent(mode_id)
|
||||
|
||||
|
|
@ -483,17 +501,12 @@ class VibeAcpAgentLoop(AcpAgent):
|
|||
self._create_approval_callback(session.id)
|
||||
)
|
||||
|
||||
return SetSessionModeResponse()
|
||||
|
||||
@override
|
||||
async def set_session_model(
|
||||
self, model_id: str, session_id: str, **kwargs: Any
|
||||
) -> SetSessionModelResponse | None:
|
||||
session = self._get_session(session_id)
|
||||
return True
|
||||
|
||||
async def _apply_model_change(self, session: AcpSessionLoop, model_id: str) -> bool:
|
||||
model_aliases = [model.alias for model in session.agent_loop.config.models]
|
||||
if model_id not in model_aliases:
|
||||
return None
|
||||
return False
|
||||
|
||||
VibeConfig.save_updates({"active_model": model_id})
|
||||
|
||||
|
|
@ -504,8 +517,59 @@ class VibeAcpAgentLoop(AcpAgent):
|
|||
|
||||
await session.agent_loop.reload_with_initial_messages(base_config=new_config)
|
||||
|
||||
return True
|
||||
|
||||
@override
|
||||
async def set_session_mode(
|
||||
self, mode_id: str, session_id: str, **kwargs: Any
|
||||
) -> SetSessionModeResponse | None:
|
||||
session = self._get_session(session_id)
|
||||
|
||||
if not await self._apply_mode_change(session, mode_id):
|
||||
return None
|
||||
|
||||
return SetSessionModeResponse()
|
||||
|
||||
@override
|
||||
async def set_session_model(
|
||||
self, model_id: str, session_id: str, **kwargs: Any
|
||||
) -> SetSessionModelResponse | None:
|
||||
session = self._get_session(session_id)
|
||||
|
||||
if not await self._apply_model_change(session, model_id):
|
||||
return None
|
||||
|
||||
return SetSessionModelResponse()
|
||||
|
||||
@override
|
||||
async def set_config_option(
|
||||
self, config_id: str, session_id: str, value: str, **kwargs: Any
|
||||
) -> SetSessionConfigOptionResponse | None:
|
||||
session = self._get_session(session_id)
|
||||
|
||||
match config_id:
|
||||
case "mode":
|
||||
success = await self._apply_mode_change(session, value)
|
||||
case "model":
|
||||
success = await self._apply_model_change(session, value)
|
||||
case _:
|
||||
success = False
|
||||
|
||||
if not success:
|
||||
return None
|
||||
|
||||
profiles = list(session.agent_loop.agent_manager.available_agents.values())
|
||||
_, modes_config = make_mode_response(
|
||||
profiles, session.agent_loop.agent_profile.name
|
||||
)
|
||||
_, models_config = make_model_response(
|
||||
session.agent_loop.config.models, session.agent_loop.config.active_model
|
||||
)
|
||||
|
||||
return SetSessionConfigOptionResponse(
|
||||
config_options=[modes_config, models_config]
|
||||
)
|
||||
|
||||
@override
|
||||
async def list_sessions(
|
||||
self, cursor: str | None = None, cwd: str | None = None, **kwargs: Any
|
||||
|
|
@ -736,7 +800,13 @@ class VibeAcpAgentLoop(AcpAgent):
|
|||
|
||||
def run_acp_server() -> None:
|
||||
try:
|
||||
asyncio.run(run_agent(agent=VibeAcpAgentLoop(), use_unstable_protocol=True))
|
||||
asyncio.run(
|
||||
run_agent(
|
||||
agent=VibeAcpAgentLoop(),
|
||||
use_unstable_protocol=True,
|
||||
observers=[acp_message_observer],
|
||||
)
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
# This is expected when the server is terminated
|
||||
pass
|
||||
|
|
|
|||
97
vibe/acp/acp_logger.py
Normal file
97
vibe/acp/acp_logger.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
import json
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from cachetools import TTLCache
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from acp.connection import StreamEvent
|
||||
|
||||
ACP_LOG_DIR = Path.home() / ".vibe" / "logs" / "acp"
|
||||
ACP_LOG_FILE = ACP_LOG_DIR / "messages.jsonl"
|
||||
MAX_LOG_SIZE_BYTES = 1_000_000
|
||||
BACKUP_COUNT = 3
|
||||
|
||||
ACP_LOGGING_ENABLED_KEY = "VIBE_ACP_LOGGING_ENABLED"
|
||||
|
||||
_session_cache: TTLCache[int | str, str] = TTLCache(maxsize=1000, ttl=3600)
|
||||
_current_session: str | None = None
|
||||
_logger: logging.Logger | None = None
|
||||
|
||||
|
||||
def is_acp_logging_enabled() -> bool:
|
||||
return os.getenv(ACP_LOGGING_ENABLED_KEY, "").lower() in {"1", "true", "yes"}
|
||||
|
||||
|
||||
class JsonLineFormatter(logging.Formatter):
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
return json.dumps(record.msg, separators=(",", ":"))
|
||||
|
||||
|
||||
def _get_logger() -> logging.Logger:
|
||||
global _logger
|
||||
if _logger is not None:
|
||||
return _logger
|
||||
|
||||
ACP_LOG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
logger = logging.getLogger("acp_messages")
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.propagate = False
|
||||
|
||||
handler = RotatingFileHandler(
|
||||
ACP_LOG_FILE,
|
||||
maxBytes=MAX_LOG_SIZE_BYTES,
|
||||
backupCount=BACKUP_COUNT,
|
||||
encoding="utf-8",
|
||||
)
|
||||
handler.setFormatter(JsonLineFormatter())
|
||||
logger.addHandler(handler)
|
||||
|
||||
_logger = logger
|
||||
return _logger
|
||||
|
||||
|
||||
def _extract_session_id(message: dict) -> str | None:
|
||||
json_str = json.dumps(message)
|
||||
match = re.search(r'"(?:session_id|sessionId)":\s*"([^"]+)"', json_str)
|
||||
return match.group(1) if match else None
|
||||
|
||||
|
||||
def acp_message_observer(event: StreamEvent) -> None:
|
||||
if not is_acp_logging_enabled():
|
||||
return
|
||||
|
||||
try:
|
||||
global _current_session
|
||||
|
||||
message = event.message
|
||||
msg_id = message.get("id", "")
|
||||
|
||||
if msg_id in _session_cache:
|
||||
session_id = _session_cache[msg_id]
|
||||
else:
|
||||
session_id = _extract_session_id(message) or _current_session
|
||||
|
||||
if session_id is not None:
|
||||
_current_session = session_id
|
||||
if msg_id:
|
||||
_session_cache[msg_id] = session_id
|
||||
|
||||
log_entry: dict = {
|
||||
"ts": datetime.now(UTC).isoformat(),
|
||||
"dir": "in" if event.direction.value == "incoming" else "out",
|
||||
"msg": message,
|
||||
**({"session": session_id} if session_id else {}),
|
||||
}
|
||||
|
||||
_get_logger().info(log_entry)
|
||||
except Exception:
|
||||
pass
|
||||
|
|
@ -7,8 +7,8 @@ import sys
|
|||
|
||||
from vibe import __version__
|
||||
from vibe.core.config import VibeConfig
|
||||
from vibe.core.logger import logger
|
||||
from vibe.core.paths.config_paths import CONFIG_FILE, HISTORY_FILE, unlock_config_paths
|
||||
from vibe.core.utils import logger
|
||||
|
||||
# Configure line buffering for subprocess communication
|
||||
sys.stdout.reconfigure(line_buffering=True) # pyright: ignore[reportAttributeAccessIssue]
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ from acp.helpers import SessionUpdate, ToolCallContentVariant
|
|||
from acp.schema import ToolCallProgress
|
||||
from pydantic import BaseModel, ConfigDict, Field, SkipValidation
|
||||
|
||||
from vibe.core.logger import logger
|
||||
from vibe.core.tools.base import BaseTool, ToolError
|
||||
from vibe.core.tools.manager import ToolManager
|
||||
from vibe.core.types import ToolCallEvent, ToolResultEvent
|
||||
from vibe.core.utils import logger
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ from acp.schema import (
|
|||
|
||||
from vibe import VIBE_ROOT
|
||||
from vibe.acp.tools.base import AcpToolState, BaseAcpTool
|
||||
from vibe.core.logger import logger
|
||||
from vibe.core.tools.base import BaseToolState, InvokeContext, ToolError
|
||||
from vibe.core.tools.builtins.bash import Bash as CoreBashTool, BashArgs, BashResult
|
||||
from vibe.core.types import ToolCallEvent, ToolResultEvent, ToolStreamEvent
|
||||
from vibe.core.utils import logger
|
||||
|
||||
|
||||
class AcpBashState(BaseToolState, AcpToolState):
|
||||
|
|
@ -110,7 +110,16 @@ class Bash(CoreBashTool, BaseAcpTool[AcpBashState]):
|
|||
raise self._build_timeout_error(command, timeout)
|
||||
|
||||
@classmethod
|
||||
def tool_call_session_update(cls, event: ToolCallEvent) -> ToolCallStart:
|
||||
def tool_call_session_update(cls, event: ToolCallEvent) -> ToolCallStart | None:
|
||||
if event.args is None:
|
||||
return ToolCallStart(
|
||||
session_update="tool_call",
|
||||
title="bash",
|
||||
tool_call_id=event.tool_call_id,
|
||||
kind="execute",
|
||||
content=None,
|
||||
raw_input=None,
|
||||
)
|
||||
if not isinstance(event.args, BashArgs):
|
||||
raise ValueError(f"Unexpected tool args: {event.args}")
|
||||
|
||||
|
|
|
|||
|
|
@ -4,19 +4,18 @@ from pathlib import Path
|
|||
|
||||
from vibe import VIBE_ROOT
|
||||
from vibe.acp.tools.base import AcpToolState, BaseAcpTool
|
||||
from vibe.core.tools.base import ToolError
|
||||
from vibe.core.tools.base import BaseToolState, ToolError
|
||||
from vibe.core.tools.builtins.read_file import (
|
||||
ReadFile as CoreReadFileTool,
|
||||
ReadFileArgs,
|
||||
ReadFileResult,
|
||||
ReadFileState,
|
||||
_ReadResult,
|
||||
)
|
||||
|
||||
ReadFileResult = ReadFileResult
|
||||
|
||||
|
||||
class AcpReadFileState(ReadFileState, AcpToolState):
|
||||
class AcpReadFileState(BaseToolState, AcpToolState):
|
||||
pass
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,17 +12,16 @@ from acp.schema import (
|
|||
|
||||
from vibe import VIBE_ROOT
|
||||
from vibe.acp.tools.base import AcpToolState, BaseAcpTool
|
||||
from vibe.core.tools.base import ToolError
|
||||
from vibe.core.tools.base import BaseToolState, ToolError
|
||||
from vibe.core.tools.builtins.search_replace import (
|
||||
SearchReplace as CoreSearchReplaceTool,
|
||||
SearchReplaceArgs,
|
||||
SearchReplaceResult,
|
||||
SearchReplaceState,
|
||||
)
|
||||
from vibe.core.types import ToolCallEvent, ToolResultEvent
|
||||
|
||||
|
||||
class AcpSearchReplaceState(SearchReplaceState, AcpToolState):
|
||||
class AcpSearchReplaceState(BaseToolState, AcpToolState):
|
||||
file_backup_content: str | None = None
|
||||
|
||||
|
||||
|
|
@ -73,6 +72,15 @@ class SearchReplace(CoreSearchReplaceTool, BaseAcpTool[AcpSearchReplaceState]):
|
|||
@classmethod
|
||||
def tool_call_session_update(cls, event: ToolCallEvent) -> SessionUpdate | None:
|
||||
args = event.args
|
||||
if args is None:
|
||||
return ToolCallStart(
|
||||
session_update="tool_call",
|
||||
title="search_replace",
|
||||
tool_call_id=event.tool_call_id,
|
||||
kind="edit",
|
||||
content=None,
|
||||
raw_input=None,
|
||||
)
|
||||
if not isinstance(args, SearchReplaceArgs):
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -12,17 +12,16 @@ from acp.schema import (
|
|||
|
||||
from vibe import VIBE_ROOT
|
||||
from vibe.acp.tools.base import AcpToolState, BaseAcpTool
|
||||
from vibe.core.tools.base import ToolError
|
||||
from vibe.core.tools.base import BaseToolState, ToolError
|
||||
from vibe.core.tools.builtins.write_file import (
|
||||
WriteFile as CoreWriteFileTool,
|
||||
WriteFileArgs,
|
||||
WriteFileResult,
|
||||
WriteFileState,
|
||||
)
|
||||
from vibe.core.types import ToolCallEvent, ToolResultEvent
|
||||
|
||||
|
||||
class AcpWriteFileState(WriteFileState, AcpToolState):
|
||||
class AcpWriteFileState(BaseToolState, AcpToolState):
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -51,6 +50,15 @@ class WriteFile(CoreWriteFileTool, BaseAcpTool[AcpWriteFileState]):
|
|||
@classmethod
|
||||
def tool_call_session_update(cls, event: ToolCallEvent) -> SessionUpdate | None:
|
||||
args = event.args
|
||||
if args is None:
|
||||
return ToolCallStart(
|
||||
session_update="tool_call",
|
||||
title="write_file",
|
||||
tool_call_id=event.tool_call_id,
|
||||
kind="edit",
|
||||
content=None,
|
||||
raw_input=None,
|
||||
)
|
||||
if not isinstance(args, WriteFileArgs):
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ def tool_call_session_update(event: ToolCallEvent) -> SessionUpdate | None:
|
|||
content=content,
|
||||
tool_call_id=event.tool_call_id,
|
||||
kind=TOOL_KIND.get(event.tool_name, "other"),
|
||||
raw_input=event.args.model_dump_json(),
|
||||
raw_input=event.args.model_dump_json() if event.args else None,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,14 @@ from acp.schema import (
|
|||
AgentMessageChunk,
|
||||
AgentThoughtChunk,
|
||||
ContentToolCallContent,
|
||||
ModelInfo,
|
||||
PermissionOption,
|
||||
SessionConfigOption,
|
||||
SessionConfigOptionSelect,
|
||||
SessionConfigSelectOption,
|
||||
SessionMode,
|
||||
SessionModelState,
|
||||
SessionModeState,
|
||||
TextContentBlock,
|
||||
ToolCallProgress,
|
||||
ToolCallStart,
|
||||
|
|
@ -21,7 +27,7 @@ from vibe.core.types import CompactEndEvent, CompactStartEvent, LLMMessage
|
|||
from vibe.core.utils import compact_reduction_display
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from vibe.core.agents.manager import AgentManager
|
||||
from vibe.core.config import ModelConfig
|
||||
|
||||
|
||||
class ToolOption(StrEnum):
|
||||
|
|
@ -50,22 +56,80 @@ TOOL_OPTIONS = [
|
|||
]
|
||||
|
||||
|
||||
def agent_profile_to_acp(profile: AgentProfile) -> SessionMode:
|
||||
return SessionMode(
|
||||
id=profile.name, name=profile.display_name, description=profile.description
|
||||
def is_valid_acp_mode(profiles: list[AgentProfile], mode_name: str) -> bool:
|
||||
return any(
|
||||
p.name == mode_name and p.agent_type == AgentType.AGENT for p in profiles
|
||||
)
|
||||
|
||||
|
||||
def is_valid_acp_agent(agent_manager: AgentManager, agent_name: str) -> bool:
|
||||
return agent_name in agent_manager.available_agents
|
||||
def make_mode_response(
|
||||
profiles: list[AgentProfile], current_mode_id: str
|
||||
) -> tuple[SessionModeState, SessionConfigOption]:
|
||||
session_modes: list[SessionMode] = []
|
||||
config_options: list[SessionConfigSelectOption] = []
|
||||
|
||||
for profile in profiles:
|
||||
if profile.agent_type != AgentType.AGENT:
|
||||
continue
|
||||
session_modes.append(
|
||||
SessionMode(
|
||||
id=profile.name,
|
||||
name=profile.display_name,
|
||||
description=profile.description,
|
||||
)
|
||||
)
|
||||
config_options.append(
|
||||
SessionConfigSelectOption(
|
||||
value=profile.name,
|
||||
name=profile.display_name,
|
||||
description=profile.description,
|
||||
)
|
||||
)
|
||||
|
||||
state = SessionModeState(
|
||||
current_mode_id=current_mode_id, available_modes=session_modes
|
||||
)
|
||||
config = SessionConfigOption(
|
||||
root=SessionConfigOptionSelect(
|
||||
id="mode",
|
||||
name="Session Mode",
|
||||
current_value=current_mode_id,
|
||||
category="mode",
|
||||
type="select",
|
||||
options=config_options,
|
||||
)
|
||||
)
|
||||
return state, config
|
||||
|
||||
|
||||
def get_all_acp_session_modes(agent_manager: AgentManager) -> list[SessionMode]:
|
||||
return [
|
||||
agent_profile_to_acp(profile)
|
||||
for profile in agent_manager.available_agents.values()
|
||||
if profile.agent_type == AgentType.AGENT
|
||||
]
|
||||
def make_model_response(
|
||||
models: list[ModelConfig], current_model_id: str
|
||||
) -> tuple[SessionModelState, SessionConfigOption]:
|
||||
model_infos: list[ModelInfo] = []
|
||||
config_options: list[SessionConfigSelectOption] = []
|
||||
|
||||
for model in models:
|
||||
model_infos.append(ModelInfo(model_id=model.alias, name=model.alias))
|
||||
config_options.append(
|
||||
SessionConfigSelectOption(
|
||||
value=model.alias, name=model.alias, description=model.name
|
||||
)
|
||||
)
|
||||
|
||||
state = SessionModelState(
|
||||
current_model_id=current_model_id, available_models=model_infos
|
||||
)
|
||||
config_option = SessionConfigOption(
|
||||
root=SessionConfigOptionSelect(
|
||||
id="model",
|
||||
name="Model",
|
||||
current_value=current_model_id,
|
||||
category="model",
|
||||
type="select",
|
||||
options=config_options,
|
||||
)
|
||||
)
|
||||
return state, config_option
|
||||
|
||||
|
||||
def create_compact_start_session_update(event: CompactStartEvent) -> ToolCallStart:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue