Rename cog -> assay across codebase
- Rename files: cog_cli.py, test_cog.py, k8s/cog-*.yaml - Update all Python tool names: cog_* -> assay_* - Update FastAPI titles, MCP server names, URLs - Update K8s manifests: deployments, services, secrets, ingress - Update Docker env vars: COG_API -> ASSAY_API - Domain: cog.loop42.de -> assay.loop42.de - SQLite path: /tmp/cog_db.sqlite -> /tmp/assay_db.sqlite Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
925fff731f
commit
bf11312b4b
13
Dockerfile.test
Normal file
13
Dockerfile.test
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir httpx websockets
|
||||||
|
|
||||||
|
COPY runtime_test.py .
|
||||||
|
COPY testcases/ testcases/
|
||||||
|
|
||||||
|
ENV ASSAY_API=http://runtime:8000/api
|
||||||
|
|
||||||
|
ENTRYPOINT ["python3", "runtime_test.py"]
|
||||||
|
CMD ["testcases/fast_v4.md"]
|
||||||
@ -1,4 +1,4 @@
|
|||||||
"""Cognitive Agent Runtime — modular package.
|
"""assay — agent runtime.
|
||||||
|
|
||||||
uvicorn entrypoint: agent:app
|
uvicorn entrypoint: agent:app
|
||||||
"""
|
"""
|
||||||
@ -30,13 +30,13 @@ class NoCacheStaticMiddleware(BaseHTTPMiddleware):
|
|||||||
|
|
||||||
STATIC_DIR = Path(__file__).parent.parent / "static"
|
STATIC_DIR = Path(__file__).parent.parent / "static"
|
||||||
|
|
||||||
app = FastAPI(title="cog")
|
app = FastAPI(title="assay")
|
||||||
|
|
||||||
# Register all API + WS routes
|
# Register all API + WS routes
|
||||||
register_routes(app)
|
register_routes(app)
|
||||||
|
|
||||||
# Serve frontend from same process (fallback for non-split deploy)
|
# Serve frontend from same process (fallback for non-split deploy)
|
||||||
# When running behind cog-frontend nginx, these paths won't be hit
|
# When running behind assay-frontend nginx, these paths won't be hit
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def index():
|
async def index():
|
||||||
resp = FileResponse(STATIC_DIR / "index.html")
|
resp = FileResponse(STATIC_DIR / "index.html")
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
"""Shared database access for Thinker and Expert nodes."""
|
"""Shared database access for Thinker and Expert nodes."""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
log = logging.getLogger("runtime")
|
log = logging.getLogger("runtime")
|
||||||
|
|
||||||
DB_HOST = "mariadb-eras"
|
DB_HOST = os.environ.get("DB_HOST", "mariadb-eras")
|
||||||
DB_USER = "root"
|
DB_USER = "root"
|
||||||
DB_PASS = "root"
|
DB_PASS = "root"
|
||||||
ALLOWED_DATABASES = ("eras2_production", "plankiste_test")
|
ALLOWED_DATABASES = ("eras2_production", "plankiste_test")
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
"""Standalone MCP SSE app — proxies tool calls to cog-runtime."""
|
"""Standalone MCP SSE app — proxies tool calls to assay-runtime."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@ -20,11 +20,11 @@ logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(message
|
|||||||
log = logging.getLogger("mcp-proxy")
|
log = logging.getLogger("mcp-proxy")
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
RUNTIME_URL = os.environ.get("RUNTIME_URL", "http://cog-runtime")
|
RUNTIME_URL = os.environ.get("RUNTIME_URL", "http://assay-runtime")
|
||||||
SERVICE_TOKENS = set(filter(None, os.environ.get("SERVICE_TOKENS", "").split(",")))
|
SERVICE_TOKENS = set(filter(None, os.environ.get("SERVICE_TOKENS", "").split(",")))
|
||||||
SERVICE_TOKEN = os.environ.get("SERVICE_TOKENS", "").split(",")[0] if os.environ.get("SERVICE_TOKENS") else ""
|
SERVICE_TOKEN = os.environ.get("SERVICE_TOKENS", "").split(",")[0] if os.environ.get("SERVICE_TOKENS") else ""
|
||||||
|
|
||||||
app = FastAPI(title="cog-mcp")
|
app = FastAPI(title="assay-mcp")
|
||||||
_security = HTTPBearer()
|
_security = HTTPBearer()
|
||||||
|
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ async def health():
|
|||||||
|
|
||||||
# --- MCP Server ---
|
# --- MCP Server ---
|
||||||
|
|
||||||
mcp_server = Server("cog")
|
mcp_server = Server("assay")
|
||||||
_mcp_transport = SseServerTransport("/mcp/messages/")
|
_mcp_transport = SseServerTransport("/mcp/messages/")
|
||||||
|
|
||||||
|
|
||||||
@ -87,29 +87,29 @@ async def _proxy_post(path: str, body: dict = None) -> dict:
|
|||||||
@mcp_server.list_tools()
|
@mcp_server.list_tools()
|
||||||
async def list_tools():
|
async def list_tools():
|
||||||
return [
|
return [
|
||||||
Tool(name="cog_send", description="Send a message to the cognitive agent and get a response.",
|
Tool(name="assay_send", description="Send a message to the cognitive agent and get a response.",
|
||||||
inputSchema={"type": "object", "properties": {
|
inputSchema={"type": "object", "properties": {
|
||||||
"text": {"type": "string", "description": "Message text to send"},
|
"text": {"type": "string", "description": "Message text to send"},
|
||||||
"database": {"type": "string", "description": "Optional: database name for query_db context"},
|
"database": {"type": "string", "description": "Optional: database name for query_db context"},
|
||||||
}, "required": ["text"]}),
|
}, "required": ["text"]}),
|
||||||
Tool(name="cog_trace", description="Get recent trace events from the pipeline (HUD events, tool calls, audit).",
|
Tool(name="assay_trace", description="Get recent trace events from the pipeline (HUD events, tool calls, audit).",
|
||||||
inputSchema={"type": "object", "properties": {
|
inputSchema={"type": "object", "properties": {
|
||||||
"last": {"type": "integer", "description": "Number of recent events (default 20)", "default": 20},
|
"last": {"type": "integer", "description": "Number of recent events (default 20)", "default": 20},
|
||||||
"filter": {"type": "string", "description": "Comma-separated event types to filter (e.g. 'tool_call,controls')"},
|
"filter": {"type": "string", "description": "Comma-separated event types to filter (e.g. 'tool_call,controls')"},
|
||||||
}}),
|
}}),
|
||||||
Tool(name="cog_history", description="Get recent chat messages from the active session.",
|
Tool(name="assay_history", description="Get recent chat messages from the active session.",
|
||||||
inputSchema={"type": "object", "properties": {
|
inputSchema={"type": "object", "properties": {
|
||||||
"last": {"type": "integer", "description": "Number of recent messages (default 20)", "default": 20},
|
"last": {"type": "integer", "description": "Number of recent messages (default 20)", "default": 20},
|
||||||
}}),
|
}}),
|
||||||
Tool(name="cog_state", description="Get the current memorizer state (mood, topic, language, facts).",
|
Tool(name="assay_state", description="Get the current memorizer state (mood, topic, language, facts).",
|
||||||
inputSchema={"type": "object", "properties": {}}),
|
inputSchema={"type": "object", "properties": {}}),
|
||||||
Tool(name="cog_clear", description="Clear the active session (history, state, controls).",
|
Tool(name="assay_clear", description="Clear the active session (history, state, controls).",
|
||||||
inputSchema={"type": "object", "properties": {}}),
|
inputSchema={"type": "object", "properties": {}}),
|
||||||
Tool(name="cog_graph", description="Get the active graph definition (nodes, edges, description).",
|
Tool(name="assay_graph", description="Get the active graph definition (nodes, edges, description).",
|
||||||
inputSchema={"type": "object", "properties": {}}),
|
inputSchema={"type": "object", "properties": {}}),
|
||||||
Tool(name="cog_graph_list", description="List all available graph definitions.",
|
Tool(name="assay_graph_list", description="List all available graph definitions.",
|
||||||
inputSchema={"type": "object", "properties": {}}),
|
inputSchema={"type": "object", "properties": {}}),
|
||||||
Tool(name="cog_graph_switch", description="Switch the active graph for new sessions.",
|
Tool(name="assay_graph_switch", description="Switch the active graph for new sessions.",
|
||||||
inputSchema={"type": "object", "properties": {
|
inputSchema={"type": "object", "properties": {
|
||||||
"name": {"type": "string", "description": "Graph name to switch to"},
|
"name": {"type": "string", "description": "Graph name to switch to"},
|
||||||
}, "required": ["name"]}),
|
}, "required": ["name"]}),
|
||||||
@ -118,7 +118,7 @@ async def list_tools():
|
|||||||
|
|
||||||
@mcp_server.call_tool()
|
@mcp_server.call_tool()
|
||||||
async def call_tool(name: str, arguments: dict):
|
async def call_tool(name: str, arguments: dict):
|
||||||
if name == "cog_send":
|
if name == "assay_send":
|
||||||
text = arguments.get("text", "")
|
text = arguments.get("text", "")
|
||||||
if not text:
|
if not text:
|
||||||
return [TextContent(type="text", text="ERROR: Missing 'text' argument.")]
|
return [TextContent(type="text", text="ERROR: Missing 'text' argument.")]
|
||||||
@ -150,7 +150,7 @@ async def call_tool(name: str, arguments: dict):
|
|||||||
return [TextContent(type="text", text=f"ERROR: {result.get('detail', 'pipeline failed')}")]
|
return [TextContent(type="text", text=f"ERROR: {result.get('detail', 'pipeline failed')}")]
|
||||||
return [TextContent(type="text", text="ERROR: Pipeline timeout (30s)")]
|
return [TextContent(type="text", text="ERROR: Pipeline timeout (30s)")]
|
||||||
|
|
||||||
elif name == "cog_trace":
|
elif name == "assay_trace":
|
||||||
last = arguments.get("last", 20)
|
last = arguments.get("last", 20)
|
||||||
event_filter = arguments.get("filter", "")
|
event_filter = arguments.get("filter", "")
|
||||||
params = {"last": last}
|
params = {"last": last}
|
||||||
@ -170,38 +170,38 @@ async def call_tool(name: str, arguments: dict):
|
|||||||
lines.append(line.rstrip())
|
lines.append(line.rstrip())
|
||||||
return [TextContent(type="text", text="\n".join(lines) if lines else "(no events)")]
|
return [TextContent(type="text", text="\n".join(lines) if lines else "(no events)")]
|
||||||
|
|
||||||
elif name == "cog_history":
|
elif name == "assay_history":
|
||||||
last = arguments.get("last", 20)
|
last = arguments.get("last", 20)
|
||||||
result = await _proxy_get("/api/history", {"last": last})
|
result = await _proxy_get("/api/history", {"last": last})
|
||||||
if "error" in result:
|
if "error" in result:
|
||||||
return [TextContent(type="text", text=f"ERROR: {result['error']}")]
|
return [TextContent(type="text", text=f"ERROR: {result['error']}")]
|
||||||
return [TextContent(type="text", text=json.dumps(result.get("messages", []), indent=2))]
|
return [TextContent(type="text", text=json.dumps(result.get("messages", []), indent=2))]
|
||||||
|
|
||||||
elif name == "cog_state":
|
elif name == "assay_state":
|
||||||
result = await _proxy_get("/api/state")
|
result = await _proxy_get("/api/state")
|
||||||
if "error" in result:
|
if "error" in result:
|
||||||
return [TextContent(type="text", text=f"ERROR: {result['error']}")]
|
return [TextContent(type="text", text=f"ERROR: {result['error']}")]
|
||||||
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
||||||
|
|
||||||
elif name == "cog_clear":
|
elif name == "assay_clear":
|
||||||
result = await _proxy_post("/api/clear")
|
result = await _proxy_post("/api/clear")
|
||||||
if "error" in result:
|
if "error" in result:
|
||||||
return [TextContent(type="text", text=f"ERROR: {result['error']}")]
|
return [TextContent(type="text", text=f"ERROR: {result['error']}")]
|
||||||
return [TextContent(type="text", text="Session cleared.")]
|
return [TextContent(type="text", text="Session cleared.")]
|
||||||
|
|
||||||
elif name == "cog_graph":
|
elif name == "assay_graph":
|
||||||
result = await _proxy_get("/api/graph/active")
|
result = await _proxy_get("/api/graph/active")
|
||||||
if "error" in result:
|
if "error" in result:
|
||||||
return [TextContent(type="text", text=f"ERROR: {result['error']}")]
|
return [TextContent(type="text", text=f"ERROR: {result['error']}")]
|
||||||
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
||||||
|
|
||||||
elif name == "cog_graph_list":
|
elif name == "assay_graph_list":
|
||||||
result = await _proxy_get("/api/graph/list")
|
result = await _proxy_get("/api/graph/list")
|
||||||
if "error" in result:
|
if "error" in result:
|
||||||
return [TextContent(type="text", text=f"ERROR: {result['error']}")]
|
return [TextContent(type="text", text=f"ERROR: {result['error']}")]
|
||||||
return [TextContent(type="text", text=json.dumps(result.get("graphs", []), indent=2))]
|
return [TextContent(type="text", text=json.dumps(result.get("graphs", []), indent=2))]
|
||||||
|
|
||||||
elif name == "cog_graph_switch":
|
elif name == "assay_graph_switch":
|
||||||
gname = arguments.get("name", "")
|
gname = arguments.get("name", "")
|
||||||
if not gname:
|
if not gname:
|
||||||
return [TextContent(type="text", text="ERROR: Missing 'name' argument.")]
|
return [TextContent(type="text", text="ERROR: Missing 'name' argument.")]
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
"""MCP server for cog — exposes runtime tools to any MCP client."""
|
"""MCP server for assay — exposes runtime tools to any MCP client."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@ -12,7 +12,7 @@ log = logging.getLogger("mcp")
|
|||||||
|
|
||||||
TRACE_FILE = Path(__file__).parent.parent / "trace.jsonl"
|
TRACE_FILE = Path(__file__).parent.parent / "trace.jsonl"
|
||||||
|
|
||||||
server = Server("cog")
|
server = Server("assay")
|
||||||
|
|
||||||
# Reference to active runtime — set by api.py when WS connects
|
# Reference to active runtime — set by api.py when WS connects
|
||||||
_get_runtime = lambda: None
|
_get_runtime = lambda: None
|
||||||
@ -26,29 +26,29 @@ def set_runtime_getter(fn):
|
|||||||
@server.list_tools()
|
@server.list_tools()
|
||||||
async def list_tools():
|
async def list_tools():
|
||||||
return [
|
return [
|
||||||
Tool(name="cog_send", description="Send a message to the cognitive agent and get a response.",
|
Tool(name="assay_send", description="Send a message to the cognitive agent and get a response.",
|
||||||
inputSchema={"type": "object", "properties": {
|
inputSchema={"type": "object", "properties": {
|
||||||
"text": {"type": "string", "description": "Message text to send"},
|
"text": {"type": "string", "description": "Message text to send"},
|
||||||
"database": {"type": "string", "description": "Optional: database name for query_db context"},
|
"database": {"type": "string", "description": "Optional: database name for query_db context"},
|
||||||
}, "required": ["text"]}),
|
}, "required": ["text"]}),
|
||||||
Tool(name="cog_trace", description="Get recent trace events from the pipeline (HUD events, tool calls, audit).",
|
Tool(name="assay_trace", description="Get recent trace events from the pipeline (HUD events, tool calls, audit).",
|
||||||
inputSchema={"type": "object", "properties": {
|
inputSchema={"type": "object", "properties": {
|
||||||
"last": {"type": "integer", "description": "Number of recent events (default 20)", "default": 20},
|
"last": {"type": "integer", "description": "Number of recent events (default 20)", "default": 20},
|
||||||
"filter": {"type": "string", "description": "Comma-separated event types to filter (e.g. 'tool_call,controls')"},
|
"filter": {"type": "string", "description": "Comma-separated event types to filter (e.g. 'tool_call,controls')"},
|
||||||
}}),
|
}}),
|
||||||
Tool(name="cog_history", description="Get recent chat messages from the active session.",
|
Tool(name="assay_history", description="Get recent chat messages from the active session.",
|
||||||
inputSchema={"type": "object", "properties": {
|
inputSchema={"type": "object", "properties": {
|
||||||
"last": {"type": "integer", "description": "Number of recent messages (default 20)", "default": 20},
|
"last": {"type": "integer", "description": "Number of recent messages (default 20)", "default": 20},
|
||||||
}}),
|
}}),
|
||||||
Tool(name="cog_state", description="Get the current memorizer state (mood, topic, language, facts).",
|
Tool(name="assay_state", description="Get the current memorizer state (mood, topic, language, facts).",
|
||||||
inputSchema={"type": "object", "properties": {}}),
|
inputSchema={"type": "object", "properties": {}}),
|
||||||
Tool(name="cog_clear", description="Clear the active session (history, state, controls).",
|
Tool(name="assay_clear", description="Clear the active session (history, state, controls).",
|
||||||
inputSchema={"type": "object", "properties": {}}),
|
inputSchema={"type": "object", "properties": {}}),
|
||||||
Tool(name="cog_graph", description="Get the active graph definition (nodes, edges, description).",
|
Tool(name="assay_graph", description="Get the active graph definition (nodes, edges, description).",
|
||||||
inputSchema={"type": "object", "properties": {}}),
|
inputSchema={"type": "object", "properties": {}}),
|
||||||
Tool(name="cog_graph_list", description="List all available graph definitions.",
|
Tool(name="assay_graph_list", description="List all available graph definitions.",
|
||||||
inputSchema={"type": "object", "properties": {}}),
|
inputSchema={"type": "object", "properties": {}}),
|
||||||
Tool(name="cog_graph_switch", description="Switch the active graph for new sessions.",
|
Tool(name="assay_graph_switch", description="Switch the active graph for new sessions.",
|
||||||
inputSchema={"type": "object", "properties": {
|
inputSchema={"type": "object", "properties": {
|
||||||
"name": {"type": "string", "description": "Graph name to switch to"},
|
"name": {"type": "string", "description": "Graph name to switch to"},
|
||||||
}, "required": ["name"]}),
|
}, "required": ["name"]}),
|
||||||
@ -59,7 +59,7 @@ async def list_tools():
|
|||||||
async def call_tool(name: str, arguments: dict):
|
async def call_tool(name: str, arguments: dict):
|
||||||
runtime = _get_runtime()
|
runtime = _get_runtime()
|
||||||
|
|
||||||
if name == "cog_send":
|
if name == "assay_send":
|
||||||
if not runtime:
|
if not runtime:
|
||||||
return [TextContent(type="text", text="ERROR: No active session — someone must be connected via WebSocket first.")]
|
return [TextContent(type="text", text="ERROR: No active session — someone must be connected via WebSocket first.")]
|
||||||
text = arguments.get("text", "").strip()
|
text = arguments.get("text", "").strip()
|
||||||
@ -69,7 +69,7 @@ async def call_tool(name: str, arguments: dict):
|
|||||||
response = runtime.history[-1]["content"] if runtime.history else "(no response)"
|
response = runtime.history[-1]["content"] if runtime.history else "(no response)"
|
||||||
return [TextContent(type="text", text=response)]
|
return [TextContent(type="text", text=response)]
|
||||||
|
|
||||||
elif name == "cog_trace":
|
elif name == "assay_trace":
|
||||||
last = arguments.get("last", 20)
|
last = arguments.get("last", 20)
|
||||||
filt = arguments.get("filter", "").split(",") if arguments.get("filter") else None
|
filt = arguments.get("filter", "").split(",") if arguments.get("filter") else None
|
||||||
if not TRACE_FILE.exists():
|
if not TRACE_FILE.exists():
|
||||||
@ -107,7 +107,7 @@ async def call_tool(name: str, arguments: dict):
|
|||||||
out.append(f"{node:12} {event:20} {str(detail)[:120]}")
|
out.append(f"{node:12} {event:20} {str(detail)[:120]}")
|
||||||
return [TextContent(type="text", text="\n".join(out) if out else "(no matching events)")]
|
return [TextContent(type="text", text="\n".join(out) if out else "(no matching events)")]
|
||||||
|
|
||||||
elif name == "cog_history":
|
elif name == "assay_history":
|
||||||
if not runtime:
|
if not runtime:
|
||||||
return [TextContent(type="text", text="(no active session)")]
|
return [TextContent(type="text", text="(no active session)")]
|
||||||
last = arguments.get("last", 20)
|
last = arguments.get("last", 20)
|
||||||
@ -119,12 +119,12 @@ async def call_tool(name: str, arguments: dict):
|
|||||||
out.append("")
|
out.append("")
|
||||||
return [TextContent(type="text", text="\n".join(out) if out else "(no messages)")]
|
return [TextContent(type="text", text="\n".join(out) if out else "(no messages)")]
|
||||||
|
|
||||||
elif name == "cog_state":
|
elif name == "assay_state":
|
||||||
if not runtime:
|
if not runtime:
|
||||||
return [TextContent(type="text", text="(no active session)")]
|
return [TextContent(type="text", text="(no active session)")]
|
||||||
return [TextContent(type="text", text=json.dumps(runtime.memorizer.state, indent=2, ensure_ascii=False))]
|
return [TextContent(type="text", text=json.dumps(runtime.memorizer.state, indent=2, ensure_ascii=False))]
|
||||||
|
|
||||||
elif name == "cog_clear":
|
elif name == "assay_clear":
|
||||||
if not runtime:
|
if not runtime:
|
||||||
return [TextContent(type="text", text="ERROR: No active session.")]
|
return [TextContent(type="text", text="ERROR: No active session.")]
|
||||||
runtime.history.clear()
|
runtime.history.clear()
|
||||||
@ -134,7 +134,7 @@ async def call_tool(name: str, arguments: dict):
|
|||||||
runtime.ui_node.machines.clear()
|
runtime.ui_node.machines.clear()
|
||||||
return [TextContent(type="text", text="Session cleared.")]
|
return [TextContent(type="text", text="Session cleared.")]
|
||||||
|
|
||||||
elif name == "cog_graph":
|
elif name == "assay_graph":
|
||||||
from .engine import load_graph, get_graph_for_cytoscape
|
from .engine import load_graph, get_graph_for_cytoscape
|
||||||
from .runtime import _active_graph_name
|
from .runtime import _active_graph_name
|
||||||
graph = load_graph(_active_graph_name)
|
graph = load_graph(_active_graph_name)
|
||||||
@ -147,11 +147,11 @@ async def call_tool(name: str, arguments: dict):
|
|||||||
"audit": graph.get("audit", {}),
|
"audit": graph.get("audit", {}),
|
||||||
}, indent=2))]
|
}, indent=2))]
|
||||||
|
|
||||||
elif name == "cog_graph_list":
|
elif name == "assay_graph_list":
|
||||||
from .engine import list_graphs
|
from .engine import list_graphs
|
||||||
return [TextContent(type="text", text=json.dumps(list_graphs(), indent=2))]
|
return [TextContent(type="text", text=json.dumps(list_graphs(), indent=2))]
|
||||||
|
|
||||||
elif name == "cog_graph_switch":
|
elif name == "assay_graph_switch":
|
||||||
from .engine import load_graph
|
from .engine import load_graph
|
||||||
import agent.runtime as rt
|
import agent.runtime as rt
|
||||||
gname = arguments.get("name", "")
|
gname = arguments.get("name", "")
|
||||||
|
|||||||
@ -340,7 +340,7 @@ CRITICAL RULES:
|
|||||||
if code and len(code.split("\n")) > 0:
|
if code and len(code.split("\n")) > 0:
|
||||||
if lang in ("sql", "sqlite"):
|
if lang in ("sql", "sqlite"):
|
||||||
wrapped = f'''import sqlite3
|
wrapped = f'''import sqlite3
|
||||||
conn = sqlite3.connect("/tmp/cog_db.sqlite")
|
conn = sqlite3.connect("/tmp/assay_db.sqlite")
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
for stmt in """{code}""".split(";"):
|
for stmt in """{code}""".split(";"):
|
||||||
stmt = stmt.strip()
|
stmt = stmt.strip()
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
"""CLI helper for reading cog API — trace, history, state, send."""
|
"""CLI helper for reading assay API — trace, history, state, send."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
API = "https://cog.loop42.de"
|
API = "https://assay.loop42.de"
|
||||||
TOKEN = "7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g"
|
TOKEN = "7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g"
|
||||||
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
|
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ def graph():
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print("Usage: cog_cli.py <command> [args]")
|
print("Usage: assay_cli.py <command> [args]")
|
||||||
print(" trace [last] [event_filter] — show trace events")
|
print(" trace [last] [event_filter] — show trace events")
|
||||||
print(" history [last] — show chat history")
|
print(" history [last] — show chat history")
|
||||||
print(" state — show memorizer state")
|
print(" state — show memorizer state")
|
||||||
@ -3,10 +3,16 @@ services:
|
|||||||
build: .
|
build: .
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
volumes:
|
|
||||||
- ./agent.py:/app/agent.py
|
|
||||||
- ./static:/app/static
|
|
||||||
- ./trace.jsonl:/app/trace.jsonl
|
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
tester:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.test
|
||||||
|
environment:
|
||||||
|
- ASSAY_API=http://runtime:8000/api
|
||||||
|
depends_on:
|
||||||
|
- runtime
|
||||||
|
profiles: ["test"]
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: cog-frontend
|
name: assay-frontend
|
||||||
namespace: default
|
namespace: default
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: cog-frontend
|
app: assay-frontend
|
||||||
strategy:
|
strategy:
|
||||||
type: RollingUpdate
|
type: RollingUpdate
|
||||||
rollingUpdate:
|
rollingUpdate:
|
||||||
@ -16,11 +16,11 @@ spec:
|
|||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: cog-frontend
|
app: assay-frontend
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
image: docker.io/library/cog-frontend:latest
|
image: docker.io/library/assay-frontend:latest
|
||||||
imagePullPolicy: Never
|
imagePullPolicy: Never
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
@ -47,11 +47,11 @@ spec:
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: cog-frontend
|
name: assay-frontend
|
||||||
namespace: default
|
namespace: default
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: cog-frontend
|
app: assay-frontend
|
||||||
ports:
|
ports:
|
||||||
- port: 80
|
- port: 80
|
||||||
targetPort: 80
|
targetPort: 80
|
||||||
@ -1,7 +1,7 @@
|
|||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: cog-runtime
|
name: assay-runtime
|
||||||
annotations:
|
annotations:
|
||||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||||
@ -9,11 +9,11 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
ingressClassName: traefik
|
ingressClassName: traefik
|
||||||
tls:
|
tls:
|
||||||
- secretName: cog-tls
|
- secretName: assay-tls
|
||||||
hosts:
|
hosts:
|
||||||
- cog.loop42.de
|
- assay.loop42.de
|
||||||
rules:
|
rules:
|
||||||
- host: cog.loop42.de
|
- host: assay.loop42.de
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
# MCP SSE — separate pod, survives runtime restarts
|
# MCP SSE — separate pod, survives runtime restarts
|
||||||
@ -21,7 +21,7 @@ spec:
|
|||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: cog-mcp
|
name: assay-mcp
|
||||||
port:
|
port:
|
||||||
number: 80
|
number: 80
|
||||||
# WebSocket + REST API — runtime pod
|
# WebSocket + REST API — runtime pod
|
||||||
@ -29,28 +29,28 @@ spec:
|
|||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: cog-runtime
|
name: assay-runtime
|
||||||
port:
|
port:
|
||||||
number: 80
|
number: 80
|
||||||
- path: /api
|
- path: /api
|
||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: cog-runtime
|
name: assay-runtime
|
||||||
port:
|
port:
|
||||||
number: 80
|
number: 80
|
||||||
- path: /health
|
- path: /health
|
||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: cog-runtime
|
name: assay-runtime
|
||||||
port:
|
port:
|
||||||
number: 80
|
number: 80
|
||||||
- path: /auth
|
- path: /auth
|
||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: cog-runtime
|
name: assay-runtime
|
||||||
port:
|
port:
|
||||||
number: 80
|
number: 80
|
||||||
# Frontend — nginx, catch-all (must be last)
|
# Frontend — nginx, catch-all (must be last)
|
||||||
@ -58,6 +58,6 @@ spec:
|
|||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: cog-frontend
|
name: assay-frontend
|
||||||
port:
|
port:
|
||||||
number: 80
|
number: 80
|
||||||
@ -1,13 +1,13 @@
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: cog-mcp
|
name: assay-mcp
|
||||||
namespace: default
|
namespace: default
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: cog-mcp
|
app: assay-mcp
|
||||||
strategy:
|
strategy:
|
||||||
type: RollingUpdate
|
type: RollingUpdate
|
||||||
rollingUpdate:
|
rollingUpdate:
|
||||||
@ -16,11 +16,11 @@ spec:
|
|||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: cog-mcp
|
app: assay-mcp
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: mcp
|
- name: mcp
|
||||||
image: docker.io/library/loop42-agent:v0.13.0
|
image: docker.io/library/loop42-agent:v0.18.0
|
||||||
imagePullPolicy: Never
|
imagePullPolicy: Never
|
||||||
command: ["uvicorn", "agent.mcp_app:app", "--host", "0.0.0.0", "--port", "8001"]
|
command: ["uvicorn", "agent.mcp_app:app", "--host", "0.0.0.0", "--port", "8001"]
|
||||||
ports:
|
ports:
|
||||||
@ -29,10 +29,10 @@ spec:
|
|||||||
- name: SERVICE_TOKENS
|
- name: SERVICE_TOKENS
|
||||||
value: 7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g
|
value: 7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g
|
||||||
- name: RUNTIME_URL
|
- name: RUNTIME_URL
|
||||||
value: "http://cog-runtime"
|
value: "http://assay-runtime"
|
||||||
envFrom:
|
envFrom:
|
||||||
- secretRef:
|
- secretRef:
|
||||||
name: cog-runtime-env
|
name: assay-runtime-env
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /health
|
path: /health
|
||||||
@ -56,11 +56,11 @@ spec:
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: cog-mcp
|
name: assay-mcp
|
||||||
namespace: default
|
namespace: default
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: cog-mcp
|
app: assay-mcp
|
||||||
ports:
|
ports:
|
||||||
- port: 80
|
- port: 80
|
||||||
targetPort: 8001
|
targetPort: 8001
|
||||||
@ -1,13 +1,13 @@
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: cog-runtime
|
name: assay-runtime
|
||||||
namespace: default
|
namespace: default
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: cog-runtime
|
app: assay-runtime
|
||||||
strategy:
|
strategy:
|
||||||
type: RollingUpdate
|
type: RollingUpdate
|
||||||
rollingUpdate:
|
rollingUpdate:
|
||||||
@ -16,11 +16,11 @@ spec:
|
|||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: cog-runtime
|
app: assay-runtime
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: agent
|
- name: agent
|
||||||
image: docker.io/library/loop42-agent:v0.13.0
|
image: docker.io/library/loop42-agent:v0.18.0
|
||||||
imagePullPolicy: Never
|
imagePullPolicy: Never
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8000
|
- containerPort: 8000
|
||||||
@ -31,7 +31,7 @@ spec:
|
|||||||
value: 7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g
|
value: 7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g
|
||||||
envFrom:
|
envFrom:
|
||||||
- secretRef:
|
- secretRef:
|
||||||
name: cog-runtime-env
|
name: assay-runtime-env
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /health
|
path: /health
|
||||||
@ -55,11 +55,11 @@ spec:
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: cog-runtime
|
name: assay-runtime
|
||||||
namespace: default
|
namespace: default
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: cog-runtime
|
app: assay-runtime
|
||||||
ports:
|
ports:
|
||||||
- port: 80
|
- port: 80
|
||||||
targetPort: 8000
|
targetPort: 8000
|
||||||
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Cog Runtime Test Runner — parses testcases/*.md and executes against live API.
|
Assay Runtime Test Runner — parses testcases/*.md and executes against live API.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
pytest testcases/ -v # run all testcases
|
pytest testcases/ -v # run all testcases
|
||||||
@ -21,7 +21,7 @@ if sys.platform == "win32":
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
API = "https://cog.loop42.de/api"
|
API = os.environ.get("ASSAY_API", "https://assay.loop42.de/api")
|
||||||
TOKEN = "7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g"
|
TOKEN = "7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g"
|
||||||
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
|
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ def _parse_command(text: str) -> dict | None:
|
|||||||
|
|
||||||
# --- API client ---
|
# --- API client ---
|
||||||
|
|
||||||
class CogClient:
|
class AssayClient:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.client = httpx.Client(timeout=90)
|
self.client = httpx.Client(timeout=90)
|
||||||
self.last_response = ""
|
self.last_response = ""
|
||||||
@ -430,9 +430,9 @@ class StepResult:
|
|||||||
detail: str = ""
|
detail: str = ""
|
||||||
|
|
||||||
|
|
||||||
class CogTestRunner:
|
class AssayTestRunner:
|
||||||
def __init__(self, on_result=None):
|
def __init__(self, on_result=None):
|
||||||
self.client = CogClient()
|
self.client = AssayClient()
|
||||||
self._on_result = on_result # callback(result_dict) per check
|
self._on_result = on_result # callback(result_dict) per check
|
||||||
|
|
||||||
def run(self, testcase: dict) -> list[dict]:
|
def run(self, testcase: dict) -> list[dict]:
|
||||||
@ -568,7 +568,7 @@ def run_standalone(paths: list[Path] = None):
|
|||||||
print(f" {r['detail']}")
|
print(f" {r['detail']}")
|
||||||
_push_status("step_result", suite=suite_name, result=r)
|
_push_status("step_result", suite=suite_name, result=r)
|
||||||
|
|
||||||
runner = CogTestRunner(on_result=_on_result)
|
runner = AssayTestRunner(on_result=_on_result)
|
||||||
results = runner.run(tc)
|
results = runner.run(tc)
|
||||||
all_results[tc["name"]] = results
|
all_results[tc["name"]] = results
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"""Test script for cog runtime API. Run with: .venv/Scripts/python.exe test_cog.py"""
|
"""Test script for assay runtime API. Run with: .venv/Scripts/python.exe test_assay.py"""
|
||||||
import httpx, sys, time
|
import httpx, sys, time
|
||||||
|
|
||||||
API = "https://cog.loop42.de/api"
|
API = "https://assay.loop42.de/api"
|
||||||
TOKEN = "7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g"
|
TOKEN = "7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g"
|
||||||
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
|
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
|
||||||
|
|
||||||
@ -21,12 +21,12 @@ tests = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
clear()
|
clear()
|
||||||
print("=== COG TEST RUN ===\n")
|
print("=== ASSAY TEST RUN ===\n")
|
||||||
|
|
||||||
for i, (msg, _) in enumerate(tests, 1):
|
for i, (msg, _) in enumerate(tests, 1):
|
||||||
print(f"--- {i}. USER: {msg}")
|
print(f"--- {i}. USER: {msg}")
|
||||||
resp, memo = send(msg)
|
resp, memo = send(msg)
|
||||||
print(f" COG: {resp}")
|
print(f" ASSAY: {resp}")
|
||||||
print(f" MEMO: name={memo.get('user_name')} mood={memo.get('user_mood')} topic={memo.get('topic')}")
|
print(f" MEMO: name={memo.get('user_name')} mood={memo.get('user_mood')} topic={memo.get('topic')}")
|
||||||
print()
|
print()
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
python run_all.py # local only
|
python run_all.py # local only
|
||||||
python run_all.py --report https://cog.loop42.de # + push to frontend
|
python run_all.py --report https://assay.loop42.de # + push to frontend
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
"""Integration tests — send real messages through the live runtime pipeline.
|
"""Integration tests — send real messages through the live runtime pipeline.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
python run_integration.py --url https://cog.loop42.de --graph v1-current
|
python run_integration.py --url https://assay.loop42.de --graph v1-current
|
||||||
python run_integration.py --url https://cog.loop42.de --graph v2-director-drives
|
python run_integration.py --url https://assay.loop42.de --graph v2-director-drives
|
||||||
python run_integration.py --url https://cog.loop42.de --graph both
|
python run_integration.py --url https://assay.loop42.de --graph both
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@ -30,7 +30,7 @@ from harness import NodeTestRunner
|
|||||||
|
|
||||||
# --- Config ---
|
# --- Config ---
|
||||||
|
|
||||||
DEFAULT_URL = "https://cog.loop42.de"
|
DEFAULT_URL = "https://assay.loop42.de"
|
||||||
TOKEN = "7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g"
|
TOKEN = "7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g"
|
||||||
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
|
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
|
||||||
|
|
||||||
|
|||||||
@ -1,98 +1,6 @@
|
|||||||
{
|
{
|
||||||
"timestamp": "2026-03-30 00:02:55",
|
"timestamp": "2026-03-30 21:04:54",
|
||||||
"testcases": {
|
"testcases": {
|
||||||
"Artifact System": [
|
|
||||||
{
|
|
||||||
"step": "Setup",
|
|
||||||
"check": "clear",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "cleared"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Query produces data_table artifact",
|
|
||||||
"check": "send: show me 3 customers in a table",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "response: The database contains information for three customers: Kathrin Jager, Leon Schre"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Query produces data_table artifact",
|
|
||||||
"check": "trace: has tool_call",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "found event 'tool_call'"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Query produces data_table artifact",
|
|
||||||
"check": "response: length > 10",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "length 138 > 10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Entity detail via card",
|
|
||||||
"check": "send: show me details for customer 1",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "response: ```tool_code\nquery_db({\"query\":\"SELECT * FROM customers WHERE customer_id = 1\"})"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Entity detail via card",
|
|
||||||
"check": "trace: has tool_call",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "found event 'tool_call'"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Entity detail via card",
|
|
||||||
"check": "response: length > 10",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "length 84 > 10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Action bar via buttons",
|
|
||||||
"check": "send: create two buttons on my dashboard: Refr",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "response: I have added the 'Refresh' and 'Export' buttons to your dashboard. These buttons"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Action bar via buttons",
|
|
||||||
"check": "actions: length >= 2",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "2 actions >= 2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Action bar via buttons",
|
|
||||||
"check": "actions: any action contains \"refresh\" or \"Refresh\"",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "found 'refresh' in actions"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Machine artifact",
|
|
||||||
"check": "send: create a machine called \"flow\" with init",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "response: OK, I've created a new interactive machine called 'flow' with the initial state "
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Machine artifact",
|
|
||||||
"check": "trace: has machine_created",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "found event 'machine_created'"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Query after buttons survive",
|
|
||||||
"check": "send: how many customers are there?",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "response: There are 693 customers in the database.\n"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Query after buttons survive",
|
|
||||||
"check": "response: length > 5",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "length 41 > 5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Query after buttons survive",
|
|
||||||
"check": "actions: any action contains \"refresh\" or \"Refresh\"",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "found 'refresh' in actions"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Fast v4": [
|
"Fast v4": [
|
||||||
{
|
{
|
||||||
"step": "Setup",
|
"step": "Setup",
|
||||||
@ -104,19 +12,19 @@
|
|||||||
"step": "Reflex",
|
"step": "Reflex",
|
||||||
"check": "send: hi!",
|
"check": "send: hi!",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "response: Hey Nico! 👋 How can I help you today?\n"
|
"detail": "response: Hey there! 👋 What's on your mind today?\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Reflex",
|
"step": "Reflex",
|
||||||
"check": "response: length > 2",
|
"check": "response: length > 2",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "length 38 > 2"
|
"detail": "length 40 > 2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "PA routes to expert",
|
"step": "PA routes to expert",
|
||||||
"check": "send: show me 3 customers",
|
"check": "send: show me 3 customers",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "response: Alright, I've fetched 3 customer records for you. You can see the ID, Name detai"
|
"detail": "response: I'm sorry, but I'm still having trouble connecting to the customer database 😞. I"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "PA routes to expert",
|
"step": "PA routes to expert",
|
||||||
@ -134,13 +42,13 @@
|
|||||||
"step": "PA routes to expert",
|
"step": "PA routes to expert",
|
||||||
"check": "response: length > 10",
|
"check": "response: length > 10",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "length 181 > 10"
|
"detail": "length 126 > 10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "German query",
|
"step": "German query",
|
||||||
"check": "send: Zeig mir alle Tabellen in der Datenbank",
|
"check": "send: Zeig mir alle Tabellen in der Datenbank",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "response: Okay, ich habe eine Liste aller Tabellen in der \"eras2_production\" Datenbank abg"
|
"detail": "response: Momentan kann ich keine Verbindung zur Datenbank herstellen, um Tabellen aufzuli"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "German query",
|
"step": "German query",
|
||||||
@ -152,13 +60,13 @@
|
|||||||
"step": "German query",
|
"step": "German query",
|
||||||
"check": "response: length > 10",
|
"check": "response: length > 10",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "length 303 > 10"
|
"detail": "length 123 > 10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Schema discovery",
|
"step": "Schema discovery",
|
||||||
"check": "send: describe the kunden table",
|
"check": "send: describe the kunden table",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "response: The `kunden` table stores customer information, including names, customer number"
|
"detail": "response: I am still unable to access the database, but I will try again to describe the \""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Schema discovery",
|
"step": "Schema discovery",
|
||||||
@ -170,13 +78,13 @@
|
|||||||
"step": "Schema discovery",
|
"step": "Schema discovery",
|
||||||
"check": "response: length > 10",
|
"check": "response: length > 10",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "length 391 > 10"
|
"detail": "length 95 > 10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Count query (cached schema)",
|
"step": "Count query (cached schema)",
|
||||||
"check": "send: how many customers are there?",
|
"check": "send: how many customers are there?",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "response: There are 693 customers in the database.\n"
|
"detail": "response: I'm still having trouble connecting to the database. The connection is unstable."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Count query (cached schema)",
|
"step": "Count query (cached schema)",
|
||||||
@ -188,13 +96,13 @@
|
|||||||
"step": "Count query (cached schema)",
|
"step": "Count query (cached schema)",
|
||||||
"check": "response: length > 5",
|
"check": "response: length > 5",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "length 41 > 5"
|
"detail": "length 271 > 5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Complex query",
|
"step": "Complex query",
|
||||||
"check": "send: which customers have the most devices?",
|
"check": "send: which customers have the most devices?",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "response: The query results list the top 10 customers with the most devices. Anne Bürger h"
|
"detail": "response: I am still having difficulty accessing the database. Once access is restored, I "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Complex query",
|
"step": "Complex query",
|
||||||
@ -206,13 +114,13 @@
|
|||||||
"step": "Complex query",
|
"step": "Complex query",
|
||||||
"check": "response: length > 20",
|
"check": "response: length > 20",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "length 166 > 20"
|
"detail": "length 185 > 20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Error recovery",
|
"step": "Error recovery",
|
||||||
"check": "send: SELECT * FROM nichtexistiert",
|
"check": "send: SELECT * FROM nichtexistiert",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "response: Oops! It seems like the table `nichtexistiert` doesn't exist in the database. 🤔 "
|
"detail": "response: ```text\nError: Table 'real_estate.nichtexistiert' doesn't exist\n```\n\nIt looks li"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Error recovery",
|
"step": "Error recovery",
|
||||||
@ -224,25 +132,25 @@
|
|||||||
"step": "Error recovery",
|
"step": "Error recovery",
|
||||||
"check": "response: length > 10",
|
"check": "response: length > 10",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "length 396 > 10"
|
"detail": "length 166 > 10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Memorizer",
|
"step": "Memorizer",
|
||||||
"check": "send: my name is Nico",
|
"check": "send: my name is Nico",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "response: Thanks, Nico! I'll remember that. Do you have any other questions? 😊\n"
|
"detail": "response: Nice to meet you, Nico! 👋 Now that I know your name, how can I help you today?\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Memorizer",
|
"step": "Memorizer",
|
||||||
"check": "state: facts any contains \"Nico\"",
|
"check": "state: facts any contains \"Nico\"",
|
||||||
"status": "PASS",
|
"status": "FAIL",
|
||||||
"detail": "found 'Nico' in facts"
|
"detail": "none of ['Nico'] found in facts: []"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Language switch",
|
"step": "Language switch",
|
||||||
"check": "send: Hallo wie gehts?",
|
"check": "send: Hallo wie gehts?",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "response: Mir geht es gut, danke der Nachfrage, Nico! Und selbst? Gibt es etwas, bei dem i"
|
"detail": "response: Mir geht es gut, danke der Nachfrage! 😊 Was kann ich für dich tun, Nico?"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Language switch",
|
"step": "Language switch",
|
||||||
@ -254,110 +162,18 @@
|
|||||||
"step": "Bye",
|
"step": "Bye",
|
||||||
"check": "send: ok bye",
|
"check": "send: ok bye",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "response: Bye Nico! 👋 If you need anything else, just let me know. 😊\n"
|
"detail": "response: Bis bald, Nico! 👋 Lass es mich wissen, wenn du wieder etwas brauchst."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"step": "Bye",
|
"step": "Bye",
|
||||||
"check": "response: length > 2",
|
"check": "response: length > 2",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"detail": "length 59 > 2"
|
"detail": "length 69 > 2"
|
||||||
}
|
|
||||||
],
|
|
||||||
"Dashboard Integration": [
|
|
||||||
{
|
|
||||||
"step": "Setup",
|
|
||||||
"check": "clear",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "cleared"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Expert creates buttons",
|
|
||||||
"check": "send: create two buttons on my dashboard: Repo",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "response: I have added 'Report' and 'Export' buttons to your dashboard.\n\n(UI buttons shown"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Expert creates buttons",
|
|
||||||
"check": "actions: length >= 2",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "3 actions >= 2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Expert creates buttons",
|
|
||||||
"check": "actions: any action contains \"report\" or \"Report\"",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "found 'report' in actions"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Buttons survive a query",
|
|
||||||
"check": "send: how many customers are there?",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "response: I'm running a query to count all customer IDs. One moment...\n"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Buttons survive a query",
|
|
||||||
"check": "response: length > 5",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "length 61 > 5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Buttons survive a query",
|
|
||||||
"check": "actions: any action contains \"report\" or \"Report\"",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "found 'report' in actions"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Expert creates a machine",
|
|
||||||
"check": "send: create a navigation machine called \"work",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "response: I've created the 'workflow' machine with 'start' and 'step2' states. The 'start'"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Expert creates a machine",
|
|
||||||
"check": "trace: has tool_call create_machine",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "found create_machine via machine_created event"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Expert shows data table",
|
|
||||||
"check": "send: show me 5 customers in a table",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "response: Here are five customer entries with their IDs, names, object count, and status:\n"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Expert shows data table",
|
|
||||||
"check": "trace: has tool_call",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "found event 'tool_call'"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Expert shows data table",
|
|
||||||
"check": "response: length > 10",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "length 118 > 10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Expert replaces buttons",
|
|
||||||
"check": "send: remove all buttons and create one button",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "response: I have removed the existing 'Report' and 'Export' buttons from the dashboard and"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Expert replaces buttons",
|
|
||||||
"check": "actions: length >= 1",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "2 actions >= 1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"step": "Expert replaces buttons",
|
|
||||||
"check": "actions: any action contains \"reset\" or \"Reset\"",
|
|
||||||
"status": "PASS",
|
|
||||||
"detail": "found 'reset' in actions"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"summary": {
|
"summary": {
|
||||||
"passed": 58,
|
"passed": 27,
|
||||||
"failed": 0
|
"failed": 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user