fred-runtime 2.0.3__tar.gz → 2.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/PKG-INFO +1 -1
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/app/agent_app.py +98 -9
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/cli/completion.py +19 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/cli/history_display.py +3 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/cli/pod_client.py +18 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/cli/repl.py +97 -1
- fred_runtime-2.0.4/fred_runtime/cli/repl_helpers.py +401 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/graph/graph_runtime.py +12 -8
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/react_message_codec.py +3 -4
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/react_model_adapter.py +2 -3
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/react_prompting.py +12 -4
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/react_runtime.py +14 -9
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/react_tool_binding.py +1 -2
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/react_tool_resolution.py +2 -3
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime.egg-info/PKG-INFO +1 -1
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime.egg-info/SOURCES.txt +6 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/pyproject.toml +13 -1
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_agent_app.py +2 -0
- fred_runtime-2.0.4/tests/test_conversational_memory.py +359 -0
- fred_runtime-2.0.4/tests/test_eval_collector.py +242 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_eval_trace.py +1 -2
- fred_runtime-2.0.4/tests/test_model_routing.py +572 -0
- fred_runtime-2.0.4/tests/test_pod_client.py +334 -0
- fred_runtime-2.0.4/tests/test_repl_helpers.py +123 -0
- fred_runtime-2.0.4/tests/test_token_expiry.py +218 -0
- fred_runtime-2.0.3/fred_runtime/cli/repl_helpers.py +0 -188
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/README.md +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/app/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/app/_catalogs.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/app/config.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/app/config_loader.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/app/container.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/app/context.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/app/dependencies.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/app/mcp_config.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/app/observability_factory.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/app/openai_compat_router.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/cli/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/cli/entrypoint.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/cli/kpi_display.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/cli/url_helpers.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/client.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/context_aware_tool.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/kf_base_client.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/kf_fast_text_client.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/kf_http_client.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/kf_logs_client.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/kf_markdown_media_client.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/kf_vectorsearch_client.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/kf_workspace_client.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/mcp_interceptors.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/mcp_runtime.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/mcp_toolkit.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/mcp_utils.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/structures.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/token_expiry.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/common/tool_node_utils.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/deep/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/deep/deep_runtime.py +3 -3
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/eval/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/eval/collector.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/graph/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/integrations/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/integrations/v2_runtime/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/integrations/v2_runtime/adapters.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/model_routing/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/model_routing/catalog.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/model_routing/contracts.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/model_routing/provider.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/model_routing/resolver.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/react_langchain_adapter.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/react_stream_adapter.py +2 -2
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/react_tool_loop.py +5 -5
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/react_tool_rendering.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/react_tool_utils.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/react/react_tracing.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/runtime_context.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/runtime_support/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/runtime_support/checkpoints.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/runtime_support/model_metadata.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/runtime_support/request_context_helpers.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/runtime_support/sql_checkpointer.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/runtime_support/user_token_refresher.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/support/__init__.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/support/filesystem_context.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/support/tool_approval.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime/support/tool_loop.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime.egg-info/dependency_links.txt +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime.egg-info/entry_points.txt +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime.egg-info/requires.txt +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/fred_runtime.egg-info/top_level.txt +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/setup.cfg +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_client.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_config_loader.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_context.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_graph_runtime_observability.py +1 -1
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_history.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_kf_workspace_client.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_kpi_display.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_mcp_config.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_openai_compat_router.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_smoke.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_url_helpers.py +0 -0
- {fred_runtime-2.0.3 → fred_runtime-2.0.4}/tests/test_user_token_refresher.py +0 -0
|
@@ -62,7 +62,6 @@ from fred_core.logs.log_setup import log_setup
|
|
|
62
62
|
from fred_core.logs.memory_log_store import RamLogStore
|
|
63
63
|
from fred_core.security.oidc import get_keycloak_client_id, get_keycloak_url
|
|
64
64
|
from fred_core.security.structure import KeycloakUser
|
|
65
|
-
from fred_sdk.contracts.eval import EvalStep, EvalTrace
|
|
66
65
|
from fred_sdk.contracts.context import (
|
|
67
66
|
AgentInvocationRequest,
|
|
68
67
|
AgentInvocationResult,
|
|
@@ -72,6 +71,7 @@ from fred_sdk.contracts.context import (
|
|
|
72
71
|
PortableEnvironment,
|
|
73
72
|
RuntimeContext,
|
|
74
73
|
)
|
|
74
|
+
from fred_sdk.contracts.eval import EvalStep, EvalTrace
|
|
75
75
|
from fred_sdk.contracts.execution import (
|
|
76
76
|
ExecutionGrantAction,
|
|
77
77
|
ExecutionGrantViolation,
|
|
@@ -84,6 +84,7 @@ from fred_sdk.contracts.models import (
|
|
|
84
84
|
GraphAgentDefinition,
|
|
85
85
|
MCPServerConfiguration,
|
|
86
86
|
ReActAgentDefinition,
|
|
87
|
+
TuningValue,
|
|
87
88
|
)
|
|
88
89
|
from fred_sdk.contracts.react_contract import ReActInput, ReActMessage, ReActMessageRole
|
|
89
90
|
from fred_sdk.contracts.runtime import (
|
|
@@ -95,9 +96,6 @@ from fred_sdk.contracts.runtime import (
|
|
|
95
96
|
RuntimeEvent,
|
|
96
97
|
RuntimeServices,
|
|
97
98
|
)
|
|
98
|
-
from fred_runtime.graph.graph_runtime import GraphRuntime
|
|
99
|
-
from fred_runtime.react.react_runtime import ReActRuntime
|
|
100
|
-
from fred_runtime.runtime_support.checkpoints import load_checkpoint
|
|
101
99
|
from fred_sdk.support.authored_toolsets import (
|
|
102
100
|
AuthoredToolRuntimePorts,
|
|
103
101
|
build_authored_tool_handlers,
|
|
@@ -105,6 +103,9 @@ from fred_sdk.support.authored_toolsets import (
|
|
|
105
103
|
from pydantic import BaseModel, Field, TypeAdapter, model_validator
|
|
106
104
|
|
|
107
105
|
from fred_runtime.common.kf_markdown_media_client import KfMarkdownMediaClient
|
|
106
|
+
from fred_runtime.graph.graph_runtime import GraphRuntime
|
|
107
|
+
from fred_runtime.react.react_runtime import ReActRuntime
|
|
108
|
+
from fred_runtime.runtime_support.checkpoints import load_checkpoint
|
|
108
109
|
|
|
109
110
|
from ..common.structures import AgentSettingsLike
|
|
110
111
|
from ..integrations.v2_runtime.adapters import (
|
|
@@ -754,6 +755,10 @@ class _AgentExecuteRequest(BaseModel):
|
|
|
754
755
|
default=(),
|
|
755
756
|
description="Prior conversation turns forwarded by the calling agent.",
|
|
756
757
|
)
|
|
758
|
+
inline_tuning: dict[str, TuningValue] | None = Field(
|
|
759
|
+
default=None,
|
|
760
|
+
description="Optional inline tuning overrides. Honored only in agent_id (direct template) mode.",
|
|
761
|
+
)
|
|
757
762
|
|
|
758
763
|
@model_validator(mode="after")
|
|
759
764
|
def _require_message_or_resume(self) -> "_AgentExecuteRequest":
|
|
@@ -800,6 +805,8 @@ def _to_internal_request(r: RuntimeExecuteRequest) -> "_AgentExecuteRequest":
|
|
|
800
805
|
context=r.to_legacy_context() or None,
|
|
801
806
|
checkpoint_id=r.checkpoint_id,
|
|
802
807
|
resume_payload=r.resume_payload,
|
|
808
|
+
invocation_turns=r.invocation_turns,
|
|
809
|
+
inline_tuning=r.inline_tuning,
|
|
803
810
|
)
|
|
804
811
|
|
|
805
812
|
|
|
@@ -812,6 +819,18 @@ class _AgentTemplateSummary(BaseModel):
|
|
|
812
819
|
available_mcp_servers: list[MCPServerConfiguration] = Field(default_factory=list)
|
|
813
820
|
|
|
814
821
|
|
|
822
|
+
class _McpCatalogEntry(BaseModel):
|
|
823
|
+
id: str
|
|
824
|
+
name: str
|
|
825
|
+
description: str | None = None
|
|
826
|
+
enabled: bool
|
|
827
|
+
transport: str | None = None
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
class _McpCatalogResponse(BaseModel):
|
|
831
|
+
servers: list[_McpCatalogEntry]
|
|
832
|
+
|
|
833
|
+
|
|
815
834
|
class _ResolvedAgentInstance(BaseModel):
|
|
816
835
|
agent_instance_id: str
|
|
817
836
|
template_agent_id: str
|
|
@@ -833,7 +852,7 @@ def _apply_runtime_tuning(
|
|
|
833
852
|
definition: ReActAgentDefinition | GraphAgentDefinition, tuning: AgentTuning
|
|
834
853
|
) -> ReActAgentDefinition | GraphAgentDefinition:
|
|
835
854
|
"""
|
|
836
|
-
Overlay persisted business tuning onto one registered
|
|
855
|
+
Overlay persisted business tuning onto one registered agent template.
|
|
837
856
|
|
|
838
857
|
Why this exists:
|
|
839
858
|
- control-plane stores the full effective tuning for a managed agent
|
|
@@ -847,18 +866,29 @@ def _apply_runtime_tuning(
|
|
|
847
866
|
- `definition = _apply_runtime_tuning(template_definition, resolution.tuning)`
|
|
848
867
|
"""
|
|
849
868
|
|
|
869
|
+
mcp_servers = tuning.mcp_servers
|
|
870
|
+
if tuning.selected_mcp_server_ids:
|
|
871
|
+
selected = frozenset(tuning.selected_mcp_server_ids)
|
|
872
|
+
mcp_servers = [s for s in mcp_servers if s.id in selected]
|
|
873
|
+
|
|
850
874
|
update: dict[str, object] = {
|
|
851
875
|
"role": tuning.role,
|
|
852
876
|
"description": tuning.description,
|
|
853
877
|
"tags": tuple(tuning.tags),
|
|
854
878
|
"fields": tuple(field.model_copy(deep=True) for field in tuning.fields),
|
|
855
879
|
"default_mcp_servers": tuple(
|
|
856
|
-
server.model_copy(deep=True) for server in
|
|
880
|
+
server.model_copy(deep=True) for server in mcp_servers
|
|
857
881
|
),
|
|
882
|
+
# Forward all values for all agent types so every execution surface can
|
|
883
|
+
# read admin-set tuning (graph steps via context.tuning_values, ReAct
|
|
884
|
+
# prompting via definition.tuning_values).
|
|
885
|
+
"tuning_values": dict(tuning.values),
|
|
858
886
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
887
|
+
if isinstance(definition, ReActAgentDefinition):
|
|
888
|
+
# Also overlay system_prompt_template directly for ReAct runtime compatibility.
|
|
889
|
+
system_prompt = tuning.values.get("prompts.system")
|
|
890
|
+
if isinstance(system_prompt, str) and system_prompt.strip():
|
|
891
|
+
update["system_prompt_template"] = system_prompt
|
|
862
892
|
return definition.model_copy(update=update)
|
|
863
893
|
|
|
864
894
|
|
|
@@ -921,6 +951,18 @@ async def _resolve_agent_instance(
|
|
|
921
951
|
detail=f"Unknown agent_id: {request.agent_id!r}. "
|
|
922
952
|
f"Known agents: {list(registry.keys())}",
|
|
923
953
|
)
|
|
954
|
+
if request.inline_tuning:
|
|
955
|
+
definition = _apply_runtime_tuning(
|
|
956
|
+
definition,
|
|
957
|
+
AgentTuning(
|
|
958
|
+
role=definition.role,
|
|
959
|
+
description=definition.description,
|
|
960
|
+
tags=list(definition.tags),
|
|
961
|
+
fields=list(definition.fields),
|
|
962
|
+
mcp_servers=list(definition.default_mcp_servers),
|
|
963
|
+
values=request.inline_tuning,
|
|
964
|
+
),
|
|
965
|
+
)
|
|
924
966
|
return _ResolvedExecutionTarget(
|
|
925
967
|
definition=definition,
|
|
926
968
|
effective_agent_id=definition.agent_id,
|
|
@@ -1751,11 +1793,24 @@ async def _iterate_runtime_event_payloads(
|
|
|
1751
1793
|
user_groups=ctx.get("user_groups"),
|
|
1752
1794
|
language=ctx.get("language"),
|
|
1753
1795
|
access_token=access_token,
|
|
1796
|
+
refresh_token=ctx.get("refresh_token"),
|
|
1797
|
+
access_token_expires_at=ctx.get("access_token_expires_at"),
|
|
1754
1798
|
trace_id=ctx.get("trace_id"),
|
|
1755
1799
|
correlation_id=correlation_id,
|
|
1756
1800
|
agent_instance_id=request.agent_instance_id,
|
|
1757
1801
|
template_agent_id=definition.agent_id,
|
|
1758
1802
|
execution_action=execution_action,
|
|
1803
|
+
# Chat options forwarded from the frontend RuntimeContext.
|
|
1804
|
+
# These were present in ctx but were silently dropped, causing
|
|
1805
|
+
# ContextAwareTool and all KF search helpers to always use defaults.
|
|
1806
|
+
selected_document_libraries_ids=ctx.get("selected_document_libraries_ids"),
|
|
1807
|
+
selected_document_uids=ctx.get("selected_document_uids"),
|
|
1808
|
+
selected_chat_context_ids=ctx.get("selected_chat_context_ids"),
|
|
1809
|
+
search_policy=ctx.get("search_policy"),
|
|
1810
|
+
search_rag_scope=ctx.get("search_rag_scope"),
|
|
1811
|
+
include_session_scope=ctx.get("include_session_scope"),
|
|
1812
|
+
include_corpus_scope=ctx.get("include_corpus_scope"),
|
|
1813
|
+
deep_search=ctx.get("deep_search"),
|
|
1759
1814
|
)
|
|
1760
1815
|
|
|
1761
1816
|
binding = BoundRuntimeContext(
|
|
@@ -1979,6 +2034,40 @@ def _build_agent_router(
|
|
|
1979
2034
|
for definition in registry.values()
|
|
1980
2035
|
]
|
|
1981
2036
|
|
|
2037
|
+
@router.get("/mcp-catalog")
|
|
2038
|
+
async def get_mcp_catalog() -> _McpCatalogResponse:
|
|
2039
|
+
"""
|
|
2040
|
+
Return the full MCP server catalog declared in mcp_catalog.yaml.
|
|
2041
|
+
|
|
2042
|
+
Why this endpoint exists:
|
|
2043
|
+
- control-plane drift detection needs to compare stored instance
|
|
2044
|
+
selections against the live pod catalog at listing time
|
|
2045
|
+
- returns ALL servers (enabled and disabled) so the caller can
|
|
2046
|
+
distinguish "configured but disabled" from "absent from catalog"
|
|
2047
|
+
|
|
2048
|
+
How to use it:
|
|
2049
|
+
- call from control-plane agent-instance listing to populate
|
|
2050
|
+
catalog_warnings when stored mcp_server_ids are no longer present
|
|
2051
|
+
|
|
2052
|
+
Example:
|
|
2053
|
+
- `GET /fred/agents/v2/agents/mcp-catalog`
|
|
2054
|
+
"""
|
|
2055
|
+
mcp_configuration = get_runtime_context().config.mcp_configuration
|
|
2056
|
+
if mcp_configuration is None:
|
|
2057
|
+
return _McpCatalogResponse(servers=[])
|
|
2058
|
+
return _McpCatalogResponse(
|
|
2059
|
+
servers=[
|
|
2060
|
+
_McpCatalogEntry(
|
|
2061
|
+
id=srv.id,
|
|
2062
|
+
name=srv.name,
|
|
2063
|
+
description=srv.description,
|
|
2064
|
+
enabled=srv.enabled,
|
|
2065
|
+
transport=srv.transport,
|
|
2066
|
+
)
|
|
2067
|
+
for srv in mcp_configuration.servers
|
|
2068
|
+
]
|
|
2069
|
+
)
|
|
2070
|
+
|
|
1982
2071
|
@router.get("/sessions", dependencies=_auth_deps)
|
|
1983
2072
|
async def list_sessions(user_id: str) -> list[str]:
|
|
1984
2073
|
"""
|
|
@@ -14,6 +14,7 @@ _COMMANDS: tuple[str, ...] = (
|
|
|
14
14
|
"/context",
|
|
15
15
|
"/delete-session",
|
|
16
16
|
"/delete-checkpoint",
|
|
17
|
+
"/inspect",
|
|
17
18
|
"/purge-session",
|
|
18
19
|
"/execution-context",
|
|
19
20
|
"/history",
|
|
@@ -21,17 +22,32 @@ _COMMANDS: tuple[str, ...] = (
|
|
|
21
22
|
"/login",
|
|
22
23
|
"/login-password",
|
|
23
24
|
"/mode",
|
|
25
|
+
"/run",
|
|
24
26
|
"/session",
|
|
25
27
|
"/session-info",
|
|
26
28
|
"/session-new",
|
|
27
29
|
"/sessions",
|
|
28
30
|
"/stats",
|
|
29
31
|
"/team",
|
|
32
|
+
"/tune",
|
|
33
|
+
"/tuning",
|
|
30
34
|
"/logout",
|
|
31
35
|
"/quit",
|
|
32
36
|
"/whoami",
|
|
33
37
|
)
|
|
34
38
|
|
|
39
|
+
# Scenario keywords for fred.test.assistant — used for /run tab-completion.
|
|
40
|
+
_TEST_ASSISTANT_SCENARIOS: tuple[str, ...] = (
|
|
41
|
+
"echo",
|
|
42
|
+
"error",
|
|
43
|
+
"hitl choice",
|
|
44
|
+
"hitl text",
|
|
45
|
+
"long",
|
|
46
|
+
"model planning",
|
|
47
|
+
"model routing",
|
|
48
|
+
"trace",
|
|
49
|
+
)
|
|
50
|
+
|
|
35
51
|
|
|
36
52
|
def completion_candidates(
|
|
37
53
|
line_buffer: str,
|
|
@@ -50,6 +66,9 @@ def completion_candidates(
|
|
|
50
66
|
if stripped.startswith("/mode "):
|
|
51
67
|
prefix = stripped.removeprefix("/mode ").strip()
|
|
52
68
|
return [mode for mode in ("eval", "final", "stream") if mode.startswith(prefix)]
|
|
69
|
+
if stripped.startswith("/run "):
|
|
70
|
+
prefix = stripped.removeprefix("/run ").strip()
|
|
71
|
+
return [s for s in _TEST_ASSISTANT_SCENARIOS if s.startswith(prefix)]
|
|
53
72
|
if stripped.startswith("/"):
|
|
54
73
|
return complete_slash_commands(stripped, commands=_COMMANDS)
|
|
55
74
|
return []
|
|
@@ -312,6 +312,7 @@ def run_single_turn(
|
|
|
312
312
|
stream: bool,
|
|
313
313
|
color_enabled: bool,
|
|
314
314
|
resume_payload: Any = None,
|
|
315
|
+
inline_tuning: dict[str, Any] | None = None,
|
|
315
316
|
) -> tuple[int, dict[str, Any] | None]:
|
|
316
317
|
"""
|
|
317
318
|
Execute one prompt and print the most useful runtime output.
|
|
@@ -327,6 +328,7 @@ def run_single_turn(
|
|
|
327
328
|
user_id=user_id,
|
|
328
329
|
team_id=team_id,
|
|
329
330
|
resume_payload=resume_payload,
|
|
331
|
+
inline_tuning=inline_tuning,
|
|
330
332
|
)
|
|
331
333
|
if "error" in payload:
|
|
332
334
|
print(
|
|
@@ -362,6 +364,7 @@ def run_single_turn(
|
|
|
362
364
|
user_id=user_id,
|
|
363
365
|
team_id=team_id,
|
|
364
366
|
resume_payload=resume_payload,
|
|
367
|
+
inline_tuning=inline_tuning,
|
|
365
368
|
):
|
|
366
369
|
if verbose:
|
|
367
370
|
print(json.dumps(event, ensure_ascii=False))
|
|
@@ -58,6 +58,16 @@ class AgentPodClient:
|
|
|
58
58
|
raise RuntimeError("Agent list response must be a JSON array of strings.")
|
|
59
59
|
return payload
|
|
60
60
|
|
|
61
|
+
def list_templates(self) -> list[dict[str, Any]]:
|
|
62
|
+
response = self.http_client.get(
|
|
63
|
+
f"{self.base_url}/agents/templates", headers=self._auth_headers()
|
|
64
|
+
)
|
|
65
|
+
response.raise_for_status()
|
|
66
|
+
payload = response.json()
|
|
67
|
+
if not isinstance(payload, list):
|
|
68
|
+
raise RuntimeError("Templates response must be a JSON array.")
|
|
69
|
+
return payload
|
|
70
|
+
|
|
61
71
|
def execute(
|
|
62
72
|
self,
|
|
63
73
|
*,
|
|
@@ -69,6 +79,7 @@ class AgentPodClient:
|
|
|
69
79
|
agent_instance_id: str | None = None,
|
|
70
80
|
checkpoint_id: str | None = None,
|
|
71
81
|
resume_payload: Any = None,
|
|
82
|
+
inline_tuning: dict[str, Any] | None = None,
|
|
72
83
|
) -> dict[str, Any]:
|
|
73
84
|
runtime_context: dict[str, Any] = {"user_id": user_id}
|
|
74
85
|
if team_id:
|
|
@@ -85,6 +96,8 @@ class AgentPodClient:
|
|
|
85
96
|
payload["checkpoint_id"] = checkpoint_id
|
|
86
97
|
if resume_payload is not None:
|
|
87
98
|
payload["resume_payload"] = resume_payload
|
|
99
|
+
if inline_tuning:
|
|
100
|
+
payload["inline_tuning"] = inline_tuning
|
|
88
101
|
response = self.http_client.post(
|
|
89
102
|
f"{self.base_url}/agents/execute",
|
|
90
103
|
json=payload,
|
|
@@ -142,6 +155,7 @@ class AgentPodClient:
|
|
|
142
155
|
agent_instance_id: str | None = None,
|
|
143
156
|
checkpoint_id: str | None = None,
|
|
144
157
|
resume_payload: Any = None,
|
|
158
|
+
inline_tuning: dict[str, Any] | None = None,
|
|
145
159
|
) -> list[dict[str, Any]]:
|
|
146
160
|
events: list[dict[str, Any]] = []
|
|
147
161
|
for event in self.iter_stream_events(
|
|
@@ -153,6 +167,7 @@ class AgentPodClient:
|
|
|
153
167
|
agent_instance_id=agent_instance_id,
|
|
154
168
|
checkpoint_id=checkpoint_id,
|
|
155
169
|
resume_payload=resume_payload,
|
|
170
|
+
inline_tuning=inline_tuning,
|
|
156
171
|
):
|
|
157
172
|
events.append(event)
|
|
158
173
|
return events
|
|
@@ -168,6 +183,7 @@ class AgentPodClient:
|
|
|
168
183
|
agent_instance_id: str | None = None,
|
|
169
184
|
checkpoint_id: str | None = None,
|
|
170
185
|
resume_payload: Any = None,
|
|
186
|
+
inline_tuning: dict[str, Any] | None = None,
|
|
171
187
|
) -> Iterator[dict[str, Any]]:
|
|
172
188
|
runtime_context: dict[str, Any] = {"user_id": user_id}
|
|
173
189
|
if team_id:
|
|
@@ -184,6 +200,8 @@ class AgentPodClient:
|
|
|
184
200
|
payload["checkpoint_id"] = checkpoint_id
|
|
185
201
|
if resume_payload is not None:
|
|
186
202
|
payload["resume_payload"] = resume_payload
|
|
203
|
+
if inline_tuning:
|
|
204
|
+
payload["inline_tuning"] = inline_tuning
|
|
187
205
|
with self.http_client.stream(
|
|
188
206
|
"POST",
|
|
189
207
|
f"{self.base_url}/agents/execute/stream",
|
|
@@ -33,7 +33,10 @@ from .repl_helpers import (
|
|
|
33
33
|
execution_mode_label,
|
|
34
34
|
fmt_bytes,
|
|
35
35
|
parse_mode_command,
|
|
36
|
+
parse_tuning_value,
|
|
36
37
|
print_help,
|
|
38
|
+
print_inspect,
|
|
39
|
+
print_tuning_table,
|
|
37
40
|
)
|
|
38
41
|
|
|
39
42
|
|
|
@@ -109,11 +112,21 @@ def run_interactive_chat(
|
|
|
109
112
|
current_session_id = session_id
|
|
110
113
|
current_mode: ExecutionMode = mode
|
|
111
114
|
current_team_id = team_id
|
|
115
|
+
current_inline_tuning: dict[str, Any] = {}
|
|
112
116
|
while True:
|
|
113
117
|
try:
|
|
118
|
+
tuning_badge = (
|
|
119
|
+
colorize(
|
|
120
|
+
f" ~{len(current_inline_tuning)}",
|
|
121
|
+
color=ANSI_YELLOW,
|
|
122
|
+
enabled=color_enabled,
|
|
123
|
+
)
|
|
124
|
+
if current_inline_tuning
|
|
125
|
+
else ""
|
|
126
|
+
)
|
|
114
127
|
prompt = (
|
|
115
128
|
f"{colorize(current_agent, color=ANSI_CYAN, enabled=color_enabled, bold=True)}"
|
|
116
|
-
"> "
|
|
129
|
+
f"{tuning_badge}> "
|
|
117
130
|
)
|
|
118
131
|
message = input(prompt).strip()
|
|
119
132
|
except EOFError:
|
|
@@ -1161,6 +1174,87 @@ def run_interactive_chat(
|
|
|
1161
1174
|
if message in {"/quit", "/exit"}:
|
|
1162
1175
|
return 0
|
|
1163
1176
|
|
|
1177
|
+
# ── /inspect ───────────────────────────────────────────────────────
|
|
1178
|
+
if message == "/inspect":
|
|
1179
|
+
try:
|
|
1180
|
+
templates = client.list_templates()
|
|
1181
|
+
except Exception as exc:
|
|
1182
|
+
print(
|
|
1183
|
+
colorize(
|
|
1184
|
+
f" Could not load templates: {exc}",
|
|
1185
|
+
color=ANSI_RED,
|
|
1186
|
+
enabled=color_enabled,
|
|
1187
|
+
)
|
|
1188
|
+
)
|
|
1189
|
+
continue
|
|
1190
|
+
print_inspect(templates, current_agent, color_enabled=color_enabled)
|
|
1191
|
+
continue
|
|
1192
|
+
|
|
1193
|
+
# ── /run <scenario> ────────────────────────────────────────────────
|
|
1194
|
+
if message.startswith("/run"):
|
|
1195
|
+
scenario = message.removeprefix("/run").strip()
|
|
1196
|
+
if not scenario:
|
|
1197
|
+
print(
|
|
1198
|
+
colorize(
|
|
1199
|
+
" Usage: /run <scenario> (tab-complete for available scenarios)",
|
|
1200
|
+
color=ANSI_DIM,
|
|
1201
|
+
enabled=color_enabled,
|
|
1202
|
+
)
|
|
1203
|
+
)
|
|
1204
|
+
continue
|
|
1205
|
+
message = scenario
|
|
1206
|
+
|
|
1207
|
+
# ── /tuning / /tune ────────────────────────────────────────────────
|
|
1208
|
+
if message == "/tuning":
|
|
1209
|
+
print_tuning_table(current_inline_tuning, color_enabled=color_enabled)
|
|
1210
|
+
continue
|
|
1211
|
+
if message.startswith("/tune"):
|
|
1212
|
+
arg = message.removeprefix("/tune").strip()
|
|
1213
|
+
if not arg:
|
|
1214
|
+
print_tuning_table(current_inline_tuning, color_enabled=color_enabled)
|
|
1215
|
+
continue
|
|
1216
|
+
if "=" not in arg:
|
|
1217
|
+
print(
|
|
1218
|
+
colorize(
|
|
1219
|
+
" Usage: /tune key=value (clear with key=)",
|
|
1220
|
+
color=ANSI_DIM,
|
|
1221
|
+
enabled=color_enabled,
|
|
1222
|
+
)
|
|
1223
|
+
)
|
|
1224
|
+
continue
|
|
1225
|
+
key, _, raw_val = arg.partition("=")
|
|
1226
|
+
key = key.strip()
|
|
1227
|
+
if not key:
|
|
1228
|
+
print(
|
|
1229
|
+
colorize(
|
|
1230
|
+
" Key cannot be empty.",
|
|
1231
|
+
color=ANSI_YELLOW,
|
|
1232
|
+
enabled=color_enabled,
|
|
1233
|
+
)
|
|
1234
|
+
)
|
|
1235
|
+
continue
|
|
1236
|
+
if not raw_val:
|
|
1237
|
+
current_inline_tuning.pop(key, None)
|
|
1238
|
+
print(
|
|
1239
|
+
colorize(
|
|
1240
|
+
f" Cleared tuning override for {key!r}.",
|
|
1241
|
+
color=ANSI_DIM,
|
|
1242
|
+
enabled=color_enabled,
|
|
1243
|
+
)
|
|
1244
|
+
)
|
|
1245
|
+
else:
|
|
1246
|
+
value = parse_tuning_value(raw_val)
|
|
1247
|
+
current_inline_tuning[key] = value
|
|
1248
|
+
val_repr = repr(value) if not isinstance(value, str) else f'"{value}"'
|
|
1249
|
+
print(
|
|
1250
|
+
colorize(
|
|
1251
|
+
f" Set {key} = {val_repr}",
|
|
1252
|
+
color=ANSI_GREEN,
|
|
1253
|
+
enabled=color_enabled,
|
|
1254
|
+
)
|
|
1255
|
+
)
|
|
1256
|
+
continue
|
|
1257
|
+
|
|
1164
1258
|
if message.startswith("/"):
|
|
1165
1259
|
bare = message.split()[0]
|
|
1166
1260
|
_USAGE_HINTS: dict[str, str] = {
|
|
@@ -1199,6 +1293,7 @@ def run_interactive_chat(
|
|
|
1199
1293
|
verbose=verbose,
|
|
1200
1294
|
stream=(current_mode == "stream"),
|
|
1201
1295
|
color_enabled=color_enabled,
|
|
1296
|
+
inline_tuning=current_inline_tuning or None,
|
|
1202
1297
|
)
|
|
1203
1298
|
while hitl is not None:
|
|
1204
1299
|
req = hitl.get("request") or {}
|
|
@@ -1241,6 +1336,7 @@ def run_interactive_chat(
|
|
|
1241
1336
|
stream=(current_mode == "stream"),
|
|
1242
1337
|
color_enabled=color_enabled,
|
|
1243
1338
|
resume_payload=resume_value,
|
|
1339
|
+
inline_tuning=current_inline_tuning or None,
|
|
1244
1340
|
)
|
|
1245
1341
|
if exit_code != 0:
|
|
1246
1342
|
print("The request failed. Use /help for commands or try another agent.")
|