roscoe 2.0.0__tar.gz → 2.2.0__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.
- {roscoe-2.0.0/roscoe.egg-info → roscoe-2.2.0}/PKG-INFO +1 -1
- {roscoe-2.0.0 → roscoe-2.2.0}/pyproject.toml +1 -1
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/__init__.py +1 -1
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/init_command.py +5 -5
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/main.py +6 -1
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/run_web.py +101 -77
- roscoe-2.2.0/roscoe/cli/scaffold/agent_config.yaml +36 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/docs.md +86 -19
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/wizard_gui.py +3 -0
- {roscoe-2.0.0 → roscoe-2.2.0/roscoe.egg-info}/PKG-INFO +1 -1
- roscoe-2.0.0/roscoe/cli/scaffold/agent_config.yaml +0 -188
- {roscoe-2.0.0 → roscoe-2.2.0}/LICENSE +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/README.md +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/approval/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/approval/gate.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/eval_command.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/eval_gui.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/google_auth_command.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/gui_theme.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/monitor_command.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/monitor_gui.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/pricing_command.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/pricing_gui.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/run_command.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/.env.example +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/db.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/evals/test_cases.json +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/main.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/prompts/system.txt +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/tools/my_tools.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/config/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/config/loader.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/_graph_base.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/base_connector.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/github.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/google_workspace.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/jira.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/notion.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/outlook.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/rest_api.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/servicenow.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/sharepoint.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/snowflake.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/core/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/core/agent_base.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/core/agent_result.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/core/agent_runner.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/core/executor.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/core/state.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/dataset.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/eval_runner.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/regression.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/report.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/scorers/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/scorers/_judge.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/scorers/base.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/scorers/hallucination.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/scorers/output_quality.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/scorers/tool_usage.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/llm/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/llm/base_provider.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/llm/capability_map.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/llm/provider_factory.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/memory/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/memory/conversation.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/memory/knowledge.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/memory/persistent.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/middleware/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/middleware/audit_logger.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/middleware/cost_tracker.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/middleware/rate_limiter.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/middleware/retry.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/alerts.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/dashboard.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/exporters/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/exporters/azure_monitor.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/exporters/prometheus.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/metrics.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/notifier.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/web.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/pricing.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/exec_assistant_agent/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/exec_assistant_agent/agent_config.yaml +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/exec_assistant_agent/prompts/system.txt +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/exec_assistant_agent/tools/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/exec_assistant_agent/tools/exec_tools.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/google_workspace_agent/agent_config.yaml +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/google_workspace_agent/prompts/system.txt +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/google_workspace_agent/tools/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/google_workspace_agent/tools/gws_tools.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/hr_agent/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/hr_agent/agent_config.yaml +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/hr_agent/prompts/system.txt +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/hr_agent/tools/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/hr_agent/tools/hr_tools.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/it_support_agent/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/it_support_agent/agent_config.yaml +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/it_support_agent/prompts/system.txt +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/it_support_agent/tools/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/it_support_agent/tools/it_tools.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/knowledge_base_agent/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/knowledge_base_agent/agent_config.yaml +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/knowledge_base_agent/prompts/system.txt +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/knowledge_base_agent/tools/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/knowledge_base_agent/tools/kb_tools.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/legal_agent/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/legal_agent/agent_config.yaml +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/legal_agent/prompts/system.txt +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/legal_agent/tools/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/legal_agent/tools/legal_tools.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/tools/__init__.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/tools/decorator.py +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe.egg-info/SOURCES.txt +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe.egg-info/dependency_links.txt +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe.egg-info/entry_points.txt +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe.egg-info/requires.txt +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/roscoe.egg-info/top_level.txt +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/setup.cfg +0 -0
- {roscoe-2.0.0 → roscoe-2.2.0}/tests/test_smoke.py +0 -0
|
@@ -32,6 +32,7 @@ _PROVIDERS = {
|
|
|
32
32
|
"azure_openai": {"env_key": "AZURE_OPENAI_KEY", "default_model": "gpt-4o", "base_url": None},
|
|
33
33
|
"anthropic": {"env_key": "ANTHROPIC_API_KEY", "default_model": "claude-sonnet-4-5", "base_url": None},
|
|
34
34
|
"gemini": {"env_key": "GOOGLE_API_KEY", "default_model": "gemini-1.5-pro", "base_url": None},
|
|
35
|
+
"nvidia": {"env_key": "NVIDIA_API_KEY", "default_model": "openai/gpt-oss-120b", "base_url": None},
|
|
35
36
|
"ollama": {"env_key": None, "default_model": "llama3.1", "base_url": None},
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -195,6 +196,7 @@ def _run_wizard(name: str) -> dict:
|
|
|
195
196
|
"azure_openai": "azure_openai",
|
|
196
197
|
"anthropic": "anthropic",
|
|
197
198
|
"gemini": "gemini",
|
|
199
|
+
"nvidia": "nvidia (NVIDIA NIM, free-tier models available)",
|
|
198
200
|
"ollama": "ollama (free, local, no API key)",
|
|
199
201
|
}
|
|
200
202
|
for i, p in enumerate(providers, 1):
|
|
@@ -355,10 +357,7 @@ def _apply_wizard(dest: Path, answers: dict) -> None:
|
|
|
355
357
|
tools_yaml = ", ".join(f'"{t}"' for t in answers["approval_tools"])
|
|
356
358
|
text = text.replace(
|
|
357
359
|
" # human_approval:\n"
|
|
358
|
-
" # require_approval_for
|
|
359
|
-
" # - send_email # tool function names that need sign-off\n"
|
|
360
|
-
" # - delete_record\n"
|
|
361
|
-
" # - submit_payment",
|
|
360
|
+
" # require_approval_for: [send_email, delete_record] # gate sensitive tools",
|
|
362
361
|
f" human_approval:\n"
|
|
363
362
|
f" require_approval_for: [{tools_yaml}]",
|
|
364
363
|
)
|
|
@@ -374,7 +373,7 @@ def _apply_wizard(dest: Path, answers: dict) -> None:
|
|
|
374
373
|
|
|
375
374
|
if answers["persistent_memory"]:
|
|
376
375
|
text = text.replace(
|
|
377
|
-
" enabled: false
|
|
376
|
+
" enabled: false # long-term facts per user_id",
|
|
378
377
|
" enabled: true",
|
|
379
378
|
)
|
|
380
379
|
|
|
@@ -416,6 +415,7 @@ def scaffold_project(
|
|
|
416
415
|
def _add_template_extras(dest: Path, template: str) -> None:
|
|
417
416
|
(dest / "main.py").write_text(_TEMPLATE_MAIN[template])
|
|
418
417
|
(dest / ".env.example").write_text(_TEMPLATE_ENV[template])
|
|
418
|
+
shutil.copy(SCAFFOLD_DIR / "docs.md", dest / "docs.md")
|
|
419
419
|
evals_dir = dest / "evals"
|
|
420
420
|
evals_dir.mkdir(exist_ok=True)
|
|
421
421
|
(evals_dir / "test_cases.json").write_text(json.dumps(_EXAMPLE_CASES, indent=2))
|
|
@@ -5,9 +5,14 @@ from __future__ import annotations
|
|
|
5
5
|
import click
|
|
6
6
|
|
|
7
7
|
# Load .env before any config parsing so ${ENV_VAR} references resolve.
|
|
8
|
+
# usecwd=True: search from the current working directory (the user's project),
|
|
9
|
+
# not from wherever roscoe itself is installed — without it, a non-editable
|
|
10
|
+
# install (e.g. `pip install roscoe`) walks up from inside site-packages and
|
|
11
|
+
# never finds a project's .env file at all.
|
|
8
12
|
try:
|
|
13
|
+
from dotenv import find_dotenv as _find_dotenv
|
|
9
14
|
from dotenv import load_dotenv as _load_dotenv
|
|
10
|
-
_load_dotenv()
|
|
15
|
+
_load_dotenv(_find_dotenv(usecwd=True))
|
|
11
16
|
except ImportError:
|
|
12
17
|
pass
|
|
13
18
|
|
|
@@ -110,7 +110,10 @@ def _result_payload(result: Any, state: dict[str, Any]) -> dict[str, Any]:
|
|
|
110
110
|
"tool_calls": action.get("tool_calls", [])}
|
|
111
111
|
if result.status == "error":
|
|
112
112
|
return {"type": "error", "error": str(result.error)}
|
|
113
|
-
|
|
113
|
+
# Some models occasionally stop with an empty final message (no tool_calls,
|
|
114
|
+
# no content) — never render a literally blank bubble for that.
|
|
115
|
+
output = result.output or "(no response from the model — try rephrasing)"
|
|
116
|
+
return {"type": "final", "output": output,
|
|
114
117
|
"tokens": result.total_tokens, "cost": cost, "tools": result.tool_calls}
|
|
115
118
|
|
|
116
119
|
|
|
@@ -118,105 +121,109 @@ _PAGE = r"""<!DOCTYPE html>
|
|
|
118
121
|
<html lang="en"><head>
|
|
119
122
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
120
123
|
<title>roscoe run</title>
|
|
124
|
+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
121
125
|
<style>
|
|
122
126
|
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
123
127
|
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
|
|
124
|
-
background:#
|
|
128
|
+
background:#f7f8fa;color:#1e293b;height:100vh;overflow:hidden}
|
|
125
129
|
|
|
126
|
-
/*
|
|
127
|
-
.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
.
|
|
131
|
-
.
|
|
130
|
+
/* 12-col split: 3 cols sidebar, 9 cols chat */
|
|
131
|
+
.layout{display:grid;grid-template-columns:repeat(12,1fr);height:100vh}
|
|
132
|
+
.sidebar{grid-column:span 3;background:#fff;border-right:1px solid #e2e8f0;
|
|
133
|
+
padding:32px 24px;display:flex;flex-direction:column}
|
|
134
|
+
.sidebar h1{font-size:19px;font-weight:700;margin-bottom:6px;color:#0f172a}
|
|
135
|
+
.sidebar p{font-size:13px;color:#64748b;margin-bottom:24px;line-height:1.5}
|
|
132
136
|
.field{margin-bottom:14px}
|
|
133
|
-
.field label{display:block;font-size:12px;color:#
|
|
134
|
-
.field input{width:100%;padding:10px 13px;background:#
|
|
135
|
-
border-radius:10px;color:#
|
|
137
|
+
.field label{display:block;font-size:12px;color:#64748b;margin-bottom:6px}
|
|
138
|
+
.field input{width:100%;padding:10px 13px;background:#f8fafc;border:1px solid #cbd5e1;
|
|
139
|
+
border-radius:10px;color:#0f172a;font-size:14px;outline:none}
|
|
136
140
|
.field input:focus{border-color:#2563eb}
|
|
137
|
-
.
|
|
141
|
+
.sidebar button{width:100%;margin-top:8px;padding:11px;background:#2563eb;color:#fff;border:none;
|
|
138
142
|
border-radius:10px;font-weight:600;font-size:14px;cursor:pointer}
|
|
139
|
-
.
|
|
140
|
-
.signedin{display:none;
|
|
141
|
-
.signedin b{color:#
|
|
143
|
+
.sidebar button:hover{background:#1d4ed8}
|
|
144
|
+
.signedin{display:none;font-size:13px;color:#64748b}
|
|
145
|
+
.signedin b{color:#0f172a}
|
|
146
|
+
.meta-block{margin-top:auto;padding-top:20px;border-top:1px solid #e2e8f0;font-size:11px;color:#94a3b8}
|
|
147
|
+
.meta-block div{margin-bottom:4px}
|
|
142
148
|
|
|
143
|
-
/*
|
|
144
|
-
.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
.
|
|
148
|
-
.
|
|
149
|
-
.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
.
|
|
157
|
-
.
|
|
158
|
-
.
|
|
159
|
-
.
|
|
160
|
-
.
|
|
161
|
-
.
|
|
162
|
-
.
|
|
163
|
-
.
|
|
164
|
-
|
|
165
|
-
.
|
|
166
|
-
.m.bot{align-self:flex-start;background:#1e2430;color:#e2e8f0;border-bottom-left-radius:4px}
|
|
167
|
-
.m.err{align-self:center;background:#3b0d0d;color:#f87171;font-size:12.5px}
|
|
168
|
-
.typing{align-self:flex-start;background:#1e2430;border-radius:13px;border-bottom-left-radius:4px;
|
|
169
|
-
padding:11px 15px;display:flex;gap:4px}
|
|
170
|
-
.typing span{width:6px;height:6px;border-radius:50%;background:#64748b;animation:bounce 1.2s infinite}
|
|
149
|
+
/* chat column */
|
|
150
|
+
.chat{grid-column:span 9;display:flex;flex-direction:column;min-height:0;background:#fff}
|
|
151
|
+
.chead{padding:18px 28px;border-bottom:1px solid #e2e8f0;display:flex;align-items:center;justify-content:space-between}
|
|
152
|
+
.chead .t{font-size:15px;font-weight:600;color:#0f172a}
|
|
153
|
+
.chead .s{font-size:11px;color:#94a3b8;margin-top:2px}
|
|
154
|
+
.msgs{flex:1;overflow-y:auto;padding:24px 28px;display:flex;flex-direction:column;gap:12px;background:#f7f8fa}
|
|
155
|
+
.msgs::-webkit-scrollbar{width:6px}.msgs::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:3px}
|
|
156
|
+
.m{max-width:65%;padding:10px 15px;border-radius:14px;font-size:14px;line-height:1.55;word-wrap:break-word}
|
|
157
|
+
.m.user{align-self:flex-end;background:#2563eb;color:#fff;border-bottom-right-radius:4px;white-space:pre-wrap}
|
|
158
|
+
.m.bot{align-self:flex-start;background:#fff;color:#1e293b;border:1px solid #e2e8f0;border-bottom-left-radius:4px}
|
|
159
|
+
.m.err{align-self:center;background:#fef2f2;color:#dc2626;font-size:13px;border:1px solid #fecaca}
|
|
160
|
+
.m.bot p{margin:0 0 8px}.m.bot p:last-child{margin-bottom:0}
|
|
161
|
+
.m.bot ul,.m.bot ol{margin:0 0 8px 20px}
|
|
162
|
+
.m.bot code{background:#f1f5f9;padding:1px 5px;border-radius:4px;font-size:12.5px;font-family:ui-monospace,monospace}
|
|
163
|
+
.m.bot pre{background:#0f172a;color:#e2e8f0;padding:10px 12px;border-radius:8px;overflow-x:auto;margin:0 0 8px}
|
|
164
|
+
.m.bot pre code{background:none;padding:0;color:inherit}
|
|
165
|
+
.m.bot table{border-collapse:collapse;margin:0 0 8px;font-size:13px}
|
|
166
|
+
.m.bot th,.m.bot td{border:1px solid #e2e8f0;padding:5px 9px;text-align:left}
|
|
167
|
+
.m.bot th{background:#f8fafc}
|
|
168
|
+
.m.bot a{color:#2563eb}
|
|
169
|
+
.typing{align-self:flex-start;background:#fff;border:1px solid #e2e8f0;border-radius:14px;border-bottom-left-radius:4px;
|
|
170
|
+
padding:12px 16px;display:flex;gap:4px}
|
|
171
|
+
.typing span{width:6px;height:6px;border-radius:50%;background:#94a3b8;animation:bounce 1.2s infinite}
|
|
171
172
|
.typing span:nth-child(2){animation-delay:.15s}.typing span:nth-child(3){animation-delay:.3s}
|
|
172
173
|
@keyframes bounce{0%,60%,100%{transform:translateY(0);opacity:.5}30%{transform:translateY(-4px);opacity:1}}
|
|
173
|
-
.approve{align-self:flex-start;background:#
|
|
174
|
-
.approve b{color:#
|
|
175
|
-
.approve
|
|
174
|
+
.approve{align-self:flex-start;background:#fffbeb;border:1px solid #fcd34d;border-radius:12px;padding:12px 15px;font-size:13px;max-width:70%}
|
|
175
|
+
.approve b{color:#b45309}
|
|
176
|
+
.approve .tc{margin:8px 0}
|
|
177
|
+
.approve .tc-name{font-family:ui-monospace,monospace;font-weight:600;color:#92400e;margin-bottom:4px}
|
|
178
|
+
.approve table.tc-args{border-collapse:collapse;width:100%}
|
|
179
|
+
.approve .tc-k{font-family:ui-monospace,monospace;color:#92400e;opacity:.75;padding:3px 8px 3px 0;
|
|
180
|
+
vertical-align:top;white-space:nowrap;font-size:12px}
|
|
181
|
+
.approve .tc-v{font-family:ui-monospace,monospace;color:#78350f;padding:3px 0;word-break:break-word;font-size:12px}
|
|
182
|
+
.approve button{border:none;border-radius:8px;padding:7px 15px;font-weight:600;cursor:pointer;margin-right:8px;margin-top:6px;font-size:12.5px}
|
|
176
183
|
.ok{background:#16a34a;color:#fff}.no{background:#dc2626;color:#fff}
|
|
177
|
-
.bar{padding:
|
|
178
|
-
.in{display:flex;gap:
|
|
179
|
-
.in input{flex:1;padding:
|
|
184
|
+
.bar{padding:8px 28px;font-size:11px;color:#94a3b8;border-top:1px solid #e2e8f0}
|
|
185
|
+
.in{display:flex;gap:10px;padding:16px 28px;border-top:1px solid #e2e8f0}
|
|
186
|
+
.in input{flex:1;padding:11px 15px;background:#f8fafc;border:1px solid #cbd5e1;border-radius:10px;color:#0f172a;font-size:14px;outline:none}
|
|
180
187
|
.in input:focus{border-color:#2563eb}
|
|
181
|
-
.in button{padding:
|
|
182
|
-
.in button:disabled{background:#
|
|
188
|
+
.in button{padding:11px 20px;background:#2563eb;color:#fff;border:none;border-radius:10px;font-weight:600;cursor:pointer;font-size:14px}
|
|
189
|
+
.in button:disabled{background:#cbd5e1;cursor:not-allowed}
|
|
190
|
+
|
|
191
|
+
@media (max-width:760px){
|
|
192
|
+
.layout{grid-template-columns:1fr}
|
|
193
|
+
.sidebar{grid-column:1;border-right:none;border-bottom:1px solid #e2e8f0}
|
|
194
|
+
.chat{grid-column:1}
|
|
195
|
+
}
|
|
183
196
|
</style></head><body>
|
|
184
197
|
|
|
185
|
-
<div class="
|
|
186
|
-
<div class="
|
|
198
|
+
<div class="layout">
|
|
199
|
+
<div class="sidebar">
|
|
187
200
|
<h1>Welcome</h1>
|
|
188
|
-
<p>Sign in to get personalized help, or just
|
|
201
|
+
<p>Sign in to get personalized help, or just start typing on the right to ask a question.</p>
|
|
189
202
|
<div id="loginForm">
|
|
190
203
|
<div class="field"><label>Your name</label><input id="lname" placeholder="e.g. Rhea Laloo"></div>
|
|
191
204
|
<div class="field"><label>Employee ID (optional)</label><input id="lid" placeholder="e.g. E-1042"></div>
|
|
192
205
|
<button onclick="signIn()">Continue</button>
|
|
193
206
|
</div>
|
|
194
|
-
<div class="signedin" id="signedIn">Signed in as <b id="signedName"></b
|
|
207
|
+
<div class="signedin" id="signedIn">Signed in as <b id="signedName"></b>.</div>
|
|
208
|
+
<div class="meta-block" id="metaBlock"></div>
|
|
195
209
|
</div>
|
|
196
|
-
</div>
|
|
197
|
-
|
|
198
|
-
<div class="launcher" id="launcher" onclick="togglePanel()">
|
|
199
|
-
<svg viewBox="0 0 24 24"><path d="M4 4h16a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1H8l-4.4 3.3A1 1 0 0 1 2 19.5V5a1 1 0 0 1 1-1h1z"/></svg>
|
|
200
|
-
<div class="dot"></div>
|
|
201
|
-
</div>
|
|
202
210
|
|
|
203
|
-
<div class="
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
<div class="chat">
|
|
212
|
+
<div class="chead">
|
|
213
|
+
<div><div class="t">roscoe run</div><div class="s">agent chat</div></div>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="msgs" id="msgs"><div class="m bot">Hi — I'm your agent. Ask me anything.</div></div>
|
|
216
|
+
<div class="bar" id="bar">ready</div>
|
|
217
|
+
<div class="in">
|
|
218
|
+
<input id="q" placeholder="Type a message…" onkeydown="if(event.key==='Enter')send()">
|
|
219
|
+
<button id="btn" onclick="send()">Send</button>
|
|
220
|
+
</div>
|
|
213
221
|
</div>
|
|
214
222
|
</div>
|
|
215
223
|
|
|
216
224
|
<script>
|
|
217
225
|
const msgs=document.getElementById('msgs'),q=document.getElementById('q'),btn=document.getElementById('btn'),bar=document.getElementById('bar');
|
|
218
|
-
|
|
219
|
-
let awaiting=false,userId='web-user',opened=false;
|
|
226
|
+
let awaiting=false,userId='web-user';
|
|
220
227
|
|
|
221
228
|
function signIn(){
|
|
222
229
|
const name=document.getElementById('lname').value.trim();
|
|
@@ -225,11 +232,14 @@ function signIn(){
|
|
|
225
232
|
document.getElementById('loginForm').style.display='none';
|
|
226
233
|
const s=document.getElementById('signedIn');s.style.display='block';
|
|
227
234
|
document.getElementById('signedName').textContent=name||'guest';
|
|
228
|
-
|
|
235
|
+
q.focus();
|
|
229
236
|
}
|
|
230
|
-
function togglePanel(){opened=!opened;panel.classList.toggle('open',opened);if(opened)q.focus()}
|
|
231
237
|
|
|
232
|
-
function add(cls,txt){
|
|
238
|
+
function add(cls,txt){
|
|
239
|
+
const d=document.createElement('div');d.className='m '+cls;
|
|
240
|
+
if(cls==='bot'&&window.marked){d.innerHTML=marked.parse(txt||'')}else{d.textContent=txt}
|
|
241
|
+
msgs.appendChild(d);msgs.scrollTop=msgs.scrollHeight;return d
|
|
242
|
+
}
|
|
233
243
|
function esc(s){const d=document.createElement('div');d.textContent=s==null?'':s;return d.innerHTML}
|
|
234
244
|
function lock(on){btn.disabled=on;q.disabled=on}
|
|
235
245
|
function showTyping(){const d=document.createElement('div');d.className='typing';d.innerHTML='<span></span><span></span><span></span>';msgs.appendChild(d);msgs.scrollTop=msgs.scrollHeight;return d}
|
|
@@ -252,11 +262,25 @@ function handle(ev){
|
|
|
252
262
|
else if(ev.type==='error'){add('err',ev.error);bar.textContent='error'}
|
|
253
263
|
else if(ev.type==='paused'){showApprove(ev)}
|
|
254
264
|
}
|
|
265
|
+
function fmtVal(v){
|
|
266
|
+
const s=typeof v==='string'?v:JSON.stringify(v);
|
|
267
|
+
return s.length>160?s.slice(0,160)+'…':s;
|
|
268
|
+
}
|
|
255
269
|
function showApprove(ev){
|
|
256
270
|
awaiting=true;bar.textContent='awaiting approval';
|
|
257
271
|
const box=document.createElement('div');box.className='approve';
|
|
258
272
|
let html='<b>⏸ approval required</b>';
|
|
259
|
-
(ev.tool_calls||[]).forEach(tc=>{
|
|
273
|
+
(ev.tool_calls||[]).forEach(tc=>{
|
|
274
|
+
html+='<div class="tc"><div class="tc-name">'+esc(tc.name)+'</div>';
|
|
275
|
+
const args=tc.args||{};
|
|
276
|
+
const keys=Object.keys(args);
|
|
277
|
+
if(keys.length){
|
|
278
|
+
html+='<table class="tc-args">'+keys.map(k=>
|
|
279
|
+
'<tr><td class="tc-k">'+esc(k)+'</td><td class="tc-v">'+esc(fmtVal(args[k]))+'</td></tr>'
|
|
280
|
+
).join('')+'</table>';
|
|
281
|
+
}
|
|
282
|
+
html+='</div>';
|
|
283
|
+
});
|
|
260
284
|
html+='<button class="ok">Approve</button><button class="no">Reject</button>';
|
|
261
285
|
box.innerHTML=html;msgs.appendChild(box);msgs.scrollTop=msgs.scrollHeight;
|
|
262
286
|
box.querySelector('.ok').onclick=()=>decide('approve',box);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# roscoe agent configuration — __PROJECT_NAME__
|
|
2
|
+
# Full reference for every option below: docs.md (scaffolded alongside this file).
|
|
3
|
+
# ${VAR_NAME} anywhere in this file resolves from your shell env or .env.
|
|
4
|
+
|
|
5
|
+
agent_name: __PROJECT_NAME__
|
|
6
|
+
system_prompt_file: prompts/system.txt
|
|
7
|
+
# ui_script: app.py # custom web UI `roscoe run` launches instead of
|
|
8
|
+
# the built-in widget (defaults to app.py if present)
|
|
9
|
+
|
|
10
|
+
model:
|
|
11
|
+
provider: openai # openai | azure_openai | anthropic | gemini | nvidia | ollama
|
|
12
|
+
model: gpt-4o-mini
|
|
13
|
+
api_key: ${OPENAI_API_KEY}
|
|
14
|
+
temperature: 0.1 # 0.0 = precise, 1.0 = creative
|
|
15
|
+
|
|
16
|
+
memory:
|
|
17
|
+
conversation:
|
|
18
|
+
enabled: true
|
|
19
|
+
window_size: 10 # messages kept per session
|
|
20
|
+
persistent:
|
|
21
|
+
enabled: false # long-term facts per user_id
|
|
22
|
+
backend: sqlite
|
|
23
|
+
connection: ./facts.db
|
|
24
|
+
|
|
25
|
+
middleware:
|
|
26
|
+
retry:
|
|
27
|
+
max_attempts: 3
|
|
28
|
+
rate_limiter:
|
|
29
|
+
enabled: true
|
|
30
|
+
requests_per_minute: 60
|
|
31
|
+
cost_tracking:
|
|
32
|
+
enabled: true # result.cost_usd — see `roscoe prices` in docs.md
|
|
33
|
+
audit:
|
|
34
|
+
enabled: true # logs/audit.jsonl — feeds `roscoe monitor`
|
|
35
|
+
# human_approval:
|
|
36
|
+
# require_approval_for: [send_email, delete_record] # gate sensitive tools
|
|
@@ -10,21 +10,22 @@ directly — they all work with the scaffolded project structure.
|
|
|
10
10
|
## Table of contents
|
|
11
11
|
|
|
12
12
|
1. [Project structure](#project-structure)
|
|
13
|
-
2. [
|
|
14
|
-
3. [
|
|
15
|
-
4. [
|
|
16
|
-
5. [
|
|
17
|
-
6. [
|
|
18
|
-
7. [
|
|
19
|
-
8. [
|
|
20
|
-
9. [
|
|
21
|
-
10. [
|
|
22
|
-
11. [
|
|
23
|
-
12. [
|
|
24
|
-
13. [
|
|
25
|
-
14. [
|
|
26
|
-
15. [
|
|
27
|
-
16. [
|
|
13
|
+
2. [Available templates](#available-templates)
|
|
14
|
+
3. [Writing tools](#writing-tools)
|
|
15
|
+
4. [Running the agent](#running-the-agent)
|
|
16
|
+
5. [Multi-turn conversations](#multi-turn-conversations)
|
|
17
|
+
6. [Swapping LLM providers](#swapping-llm-providers)
|
|
18
|
+
7. [Memory](#memory)
|
|
19
|
+
8. [Connectors](#connectors)
|
|
20
|
+
9. [Human-in-the-loop (HITL)](#human-in-the-loop-hitl)
|
|
21
|
+
10. [Audit log & cost tracking](#audit-log--cost-tracking)
|
|
22
|
+
11. [Monitoring dashboard](#monitoring-dashboard)
|
|
23
|
+
12. [Alerts & exporters](#alerts--exporters)
|
|
24
|
+
13. [Evals](#evals)
|
|
25
|
+
14. [Extending the cost table](#extending-the-cost-table)
|
|
26
|
+
15. [Configuration reference](#configuration-reference)
|
|
27
|
+
16. [Async usage](#async-usage)
|
|
28
|
+
17. [Troubleshooting](#troubleshooting)
|
|
28
29
|
|
|
29
30
|
---
|
|
30
31
|
|
|
@@ -46,6 +47,36 @@ __PROJECT_NAME__/
|
|
|
46
47
|
|
|
47
48
|
---
|
|
48
49
|
|
|
50
|
+
## Available templates
|
|
51
|
+
|
|
52
|
+
This project was scaffolded from one of roscoe's six built-in templates (or from
|
|
53
|
+
scratch, if you didn't pass `--template`). Each one ships with tools, a system
|
|
54
|
+
prompt, `agent_config.yaml`, and approval gates already wired up — a starting
|
|
55
|
+
point you can rename and extend, not a locked-in structure.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
roscoe init my-hr-bot --template hr_agent
|
|
59
|
+
roscoe init my-it-bot --template it_support_agent
|
|
60
|
+
roscoe init my-legal --template legal_agent
|
|
61
|
+
roscoe init my-kb --template knowledge_base_agent
|
|
62
|
+
roscoe init my-ea --template exec_assistant_agent
|
|
63
|
+
roscoe init my-gws --template google_workspace_agent
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
| Template | Use case | Connector | Approval gate |
|
|
67
|
+
|---|---|---|---|
|
|
68
|
+
| `hr_agent` | Leave, payslips, personal details | REST API | `submit_leave_request` |
|
|
69
|
+
| `it_support_agent` | Tickets, escalation, KB search | ServiceNow | `escalate_ticket` |
|
|
70
|
+
| `legal_agent` | Contract search, clause extraction, risk flags | Knowledge (RAG) | — |
|
|
71
|
+
| `knowledge_base_agent` | Q&A over Notion / SharePoint / docs | Notion + Knowledge | — (read-only) |
|
|
72
|
+
| `exec_assistant_agent` | Email, calendar, availability | Outlook (MS Graph) | `send_email`, `create_event` |
|
|
73
|
+
| `google_workspace_agent` | Gmail, Calendar, Tasks, Drive | Google Workspace (service account or OAuth2) | `send_email`, `create_event`, `create_task` |
|
|
74
|
+
|
|
75
|
+
Mixing templates is fine too — copy a tool or connector wiring from another
|
|
76
|
+
template's `tools/my_tools.py` into this project instead of starting a new one.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
49
80
|
## Writing tools
|
|
50
81
|
|
|
51
82
|
A tool is a plain Python function with type hints and a docstring:
|
|
@@ -83,7 +114,21 @@ TOOLS = [get_weather, search_docs]
|
|
|
83
114
|
|
|
84
115
|
## Running the agent
|
|
85
116
|
|
|
86
|
-
**
|
|
117
|
+
**From the CLI** — the fastest way to try the agent, no code needed:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
roscoe run # browser chat (opens automatically)
|
|
121
|
+
roscoe run --terminal # interactive chat in the terminal, streamed
|
|
122
|
+
roscoe run -m "your message" # one-shot message, prints the reply, exits
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
If this project has its own web UI script (`app.py`, or whatever `ui_script:`
|
|
126
|
+
names in `agent_config.yaml`), `roscoe run` launches that instead of the
|
|
127
|
+
built-in browser widget — so a custom login page or dashboard just works with
|
|
128
|
+
no flags. `--terminal` and `-m` always talk to the agent directly and never
|
|
129
|
+
touch a custom UI script.
|
|
130
|
+
|
|
131
|
+
**From Python — single-shot:**
|
|
87
132
|
|
|
88
133
|
```python
|
|
89
134
|
from roscoe import AgentRunner
|
|
@@ -169,6 +214,14 @@ model:
|
|
|
169
214
|
api_key: ${GOOGLE_API_KEY}
|
|
170
215
|
```
|
|
171
216
|
|
|
217
|
+
**NVIDIA NIM (free-tier models available):**
|
|
218
|
+
```yaml
|
|
219
|
+
model:
|
|
220
|
+
provider: nvidia
|
|
221
|
+
model: openai/gpt-oss-120b
|
|
222
|
+
api_key: ${NVIDIA_API_KEY}
|
|
223
|
+
```
|
|
224
|
+
|
|
172
225
|
**Azure OpenAI:**
|
|
173
226
|
```yaml
|
|
174
227
|
model:
|
|
@@ -273,7 +326,7 @@ agent = AgentRunner.from_config("agent_config.yaml", tools=TOOLS + jira.tools)
|
|
|
273
326
|
| SharePoint | `SharePointConnector` | `client_id`, `client_secret`, `tenant_id`, `site_id` |
|
|
274
327
|
| GitHub | `GitHubConnector` | `token` |
|
|
275
328
|
| Notion | `NotionConnector` | `token` |
|
|
276
|
-
| Google Workspace | `GoogleWorkspaceConnector` | `credentials_file`, `subject` |
|
|
329
|
+
| Google Workspace | `GoogleWorkspaceConnector` | `credentials_file`, `subject` (service account) — or `client_id`, `client_secret`, `refresh_token` (OAuth2, minted via `roscoe google-auth`) |
|
|
277
330
|
| Snowflake | `SnowflakeConnector` | `account`, `user`, `password`, `warehouse`, `database` |
|
|
278
331
|
|
|
279
332
|
All connectors accept an optional `transport` parameter for mocking in tests:
|
|
@@ -551,7 +604,18 @@ print(diff.deltas) # per-scorer, per-case score differences
|
|
|
551
604
|
|
|
552
605
|
## Extending the cost table
|
|
553
606
|
|
|
554
|
-
roscoe ships with rates for common models.
|
|
607
|
+
roscoe ships with rates for common models. NVIDIA and Ollama default to $0.00
|
|
608
|
+
(free-tier), so `nvidia`/`openai/gpt-oss-120b` and similar have no cost until
|
|
609
|
+
you price them.
|
|
610
|
+
|
|
611
|
+
**From the CLI** (writes to `~/.roscoe/prices.json`, applied on every run):
|
|
612
|
+
|
|
613
|
+
```bash
|
|
614
|
+
roscoe prices # desktop editor — add/edit rates per provider+model
|
|
615
|
+
roscoe prices --terminal # print the effective price table instead
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
**From Python** (in-process only, before calling `agent.run()`):
|
|
555
619
|
|
|
556
620
|
```python
|
|
557
621
|
from roscoe.middleware.cost_tracker import COST_TABLE
|
|
@@ -579,10 +643,13 @@ agent_name: my-agent # used in audit logs and monitorin
|
|
|
579
643
|
system_prompt_file: prompts/system.txt # path to system prompt file
|
|
580
644
|
# system_prompt: | # or inline
|
|
581
645
|
# You are a helpful assistant.
|
|
646
|
+
# ui_script: app.py # custom web UI `roscoe run` launches instead
|
|
647
|
+
# # of the built-in widget (defaults to app.py
|
|
648
|
+
# # if that file exists, even without this key)
|
|
582
649
|
|
|
583
650
|
# --- LLM provider ---
|
|
584
651
|
model:
|
|
585
|
-
provider: openai # openai | azure_openai | anthropic | gemini | ollama
|
|
652
|
+
provider: openai # openai | azure_openai | anthropic | gemini | nvidia | ollama
|
|
586
653
|
model: gpt-4o-mini # model name
|
|
587
654
|
api_key: ${OPENAI_API_KEY} # resolved from environment
|
|
588
655
|
temperature: 0.1 # 0.0–2.0
|
|
@@ -19,6 +19,7 @@ _PROVIDERS = [
|
|
|
19
19
|
("Azure OpenAI", "azure_openai"),
|
|
20
20
|
("Anthropic", "anthropic"),
|
|
21
21
|
("Gemini", "gemini"),
|
|
22
|
+
("NVIDIA (NIM, free-tier models available)", "nvidia"),
|
|
22
23
|
("Ollama (free, local, no API key)", "ollama"),
|
|
23
24
|
]
|
|
24
25
|
|
|
@@ -28,6 +29,7 @@ _DEFAULT_MODELS = {
|
|
|
28
29
|
"azure_openai": "gpt-4o",
|
|
29
30
|
"anthropic": "claude-sonnet-4-5",
|
|
30
31
|
"gemini": "gemini-1.5-pro",
|
|
32
|
+
"nvidia": "openai/gpt-oss-120b",
|
|
31
33
|
"ollama": "llama3.1",
|
|
32
34
|
}
|
|
33
35
|
|
|
@@ -37,6 +39,7 @@ _ENV_KEYS = {
|
|
|
37
39
|
"azure_openai": "AZURE_OPENAI_KEY",
|
|
38
40
|
"anthropic": "ANTHROPIC_API_KEY",
|
|
39
41
|
"gemini": "GOOGLE_API_KEY",
|
|
42
|
+
"nvidia": "NVIDIA_API_KEY",
|
|
40
43
|
"ollama": None,
|
|
41
44
|
}
|
|
42
45
|
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
# =============================================================================
|
|
2
|
-
# roscoe agent configuration — __PROJECT_NAME__
|
|
3
|
-
# =============================================================================
|
|
4
|
-
#
|
|
5
|
-
# This is the only file you need to configure. Everything below is read by
|
|
6
|
-
# AgentRunner.from_config() and wired automatically — no boilerplate code.
|
|
7
|
-
#
|
|
8
|
-
# Environment variables: use ${VAR_NAME} anywhere in this file. Values are
|
|
9
|
-
# resolved from your shell environment (or a .env file if you load one).
|
|
10
|
-
#
|
|
11
|
-
# Docs: https://github.com/rhealaloo45/roscoe
|
|
12
|
-
# =============================================================================
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
# ---------- Agent identity ---------------------------------------------------
|
|
16
|
-
|
|
17
|
-
agent_name: __PROJECT_NAME__
|
|
18
|
-
|
|
19
|
-
# The system prompt tells the LLM who it is and how to behave.
|
|
20
|
-
# Inline string or point to a file:
|
|
21
|
-
system_prompt_file: prompts/system.txt
|
|
22
|
-
# system_prompt: |
|
|
23
|
-
# You are a helpful assistant. Be concise.
|
|
24
|
-
|
|
25
|
-
# `roscoe run` opens a generic browser chat by default. If this project has
|
|
26
|
-
# its own web UI (a Flask app that builds the agent itself — custom login,
|
|
27
|
-
# dashboard, branded chat), roscoe defers to it instead: it looks for
|
|
28
|
-
# `app.py` in this directory and just runs it. Only set this if your file
|
|
29
|
-
# is named something else:
|
|
30
|
-
# ui_script: app.py
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
# ---------- Model ------------------------------------------------------------
|
|
34
|
-
# Swap providers by changing this block. Your Python code stays identical.
|
|
35
|
-
#
|
|
36
|
-
# Supported providers:
|
|
37
|
-
# openai — GPT-4o, GPT-4o-mini, etc.
|
|
38
|
-
# azure_openai — Azure-hosted OpenAI (needs deployment + endpoint)
|
|
39
|
-
# anthropic — Claude Sonnet, Opus, Haiku
|
|
40
|
-
# gemini — Gemini 1.5 Pro / Flash
|
|
41
|
-
# ollama — any local model (free, no API key)
|
|
42
|
-
#
|
|
43
|
-
# OpenAI-compatible endpoints (OpenRouter, Together, etc.):
|
|
44
|
-
# provider: openai
|
|
45
|
-
# base_url: https://openrouter.ai/api/v1
|
|
46
|
-
# model: meta-llama/llama-3.1-8b-instruct
|
|
47
|
-
|
|
48
|
-
model:
|
|
49
|
-
provider: openai
|
|
50
|
-
model: gpt-4o-mini
|
|
51
|
-
api_key: ${OPENAI_API_KEY}
|
|
52
|
-
temperature: 0.1
|
|
53
|
-
# max_tokens: 4096 # cap the response length
|
|
54
|
-
|
|
55
|
-
# --- Azure OpenAI example (uncomment & replace the block above) ---
|
|
56
|
-
# provider: azure_openai
|
|
57
|
-
# deployment: my-gpt4o-deployment
|
|
58
|
-
# endpoint: ${AZURE_OPENAI_ENDPOINT}
|
|
59
|
-
# api_key: ${AZURE_OPENAI_KEY}
|
|
60
|
-
# api_version: "2024-02-01"
|
|
61
|
-
|
|
62
|
-
# --- Anthropic example ---
|
|
63
|
-
# provider: anthropic
|
|
64
|
-
# model: claude-sonnet-4-5
|
|
65
|
-
# api_key: ${ANTHROPIC_API_KEY}
|
|
66
|
-
|
|
67
|
-
# --- Gemini example ---
|
|
68
|
-
# provider: gemini
|
|
69
|
-
# model: gemini-1.5-pro
|
|
70
|
-
# api_key: ${GOOGLE_API_KEY}
|
|
71
|
-
|
|
72
|
-
# --- Ollama (local, free, no key) ---
|
|
73
|
-
# provider: ollama
|
|
74
|
-
# model: llama3.1
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
# ---------- Memory -----------------------------------------------------------
|
|
78
|
-
# Conversation = short-term (per session_id, windowed).
|
|
79
|
-
# Persistent = long-term facts per user_id, stored in sqlite.
|
|
80
|
-
# Knowledge = RAG retrieval (FAISS or keyword). Set up in code, not here.
|
|
81
|
-
|
|
82
|
-
memory:
|
|
83
|
-
conversation:
|
|
84
|
-
enabled: true
|
|
85
|
-
window_size: 10 # keep last N messages per session
|
|
86
|
-
|
|
87
|
-
persistent:
|
|
88
|
-
enabled: false # flip to true to enable cross-session memory
|
|
89
|
-
backend: sqlite
|
|
90
|
-
connection: ./facts.db # file path, or ":memory:" for throwaway
|
|
91
|
-
# max_facts: 50 # cap stored facts per user (default: unlimited)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
# ---------- Middleware -------------------------------------------------------
|
|
95
|
-
# All middleware is automatic — set it here, forget about it in code.
|
|
96
|
-
|
|
97
|
-
middleware:
|
|
98
|
-
|
|
99
|
-
# Retry — retries failed LLM calls with exponential backoff.
|
|
100
|
-
# Handles transient errors (rate limits, timeouts, 500s) automatically.
|
|
101
|
-
retry:
|
|
102
|
-
max_attempts: 3 # total attempts (1 = no retry)
|
|
103
|
-
# wait_multiplier: 1 # backoff multiplier in seconds
|
|
104
|
-
# wait_max: 10 # max wait between retries
|
|
105
|
-
|
|
106
|
-
# Rate limiter — token-bucket per provider. Prevents thrashing API limits
|
|
107
|
-
# when multiple agents share the same provider.
|
|
108
|
-
# Ollama is always skipped (no external limit).
|
|
109
|
-
rate_limiter:
|
|
110
|
-
enabled: true
|
|
111
|
-
requests_per_minute: 60 # adjust based on your API tier
|
|
112
|
-
# rpm: 60 # alias for requests_per_minute
|
|
113
|
-
|
|
114
|
-
# Cost tracking — estimates USD cost from token counts.
|
|
115
|
-
# Shows up in result.cost_usd and the audit log.
|
|
116
|
-
# Built-in rates: gpt-4o, gpt-4o-mini, claude-sonnet-4-5, gemini-1.5-pro.
|
|
117
|
-
# Prices drift — add or correct your own rates for ANY provider (no code):
|
|
118
|
-
# roscoe prices # desktop editor, saves to ~/.roscoe/prices.json
|
|
119
|
-
cost_tracking:
|
|
120
|
-
enabled: true
|
|
121
|
-
|
|
122
|
-
# Tool guardrails — hard timeout per tool call, plus an error budget so a
|
|
123
|
-
# failing tool is refused instead of retried every iteration. Both optional.
|
|
124
|
-
# tools:
|
|
125
|
-
# timeout_seconds: 30 # kill a single tool call after N seconds
|
|
126
|
-
# max_failures: 3 # disable a tool for the run after N failures
|
|
127
|
-
|
|
128
|
-
# Audit logging — writes one JSONL line per run to logs/audit.jsonl.
|
|
129
|
-
# Non-blocking (async). Contains: run_id, agent, provider, model, tokens,
|
|
130
|
-
# cost, status, latency. Feed to `roscoe monitor` for dashboards.
|
|
131
|
-
audit:
|
|
132
|
-
enabled: true
|
|
133
|
-
# path: logs/audit.jsonl # default path; override if needed
|
|
134
|
-
|
|
135
|
-
# Human approval — pause the agent before running sensitive tools.
|
|
136
|
-
# The run returns status="paused"; call agent.resume(run_id, "approve")
|
|
137
|
-
# to continue, or "reject" / "modify" to block / change args.
|
|
138
|
-
# human_approval:
|
|
139
|
-
# require_approval_for:
|
|
140
|
-
# - send_email # tool function names that need sign-off
|
|
141
|
-
# - delete_record
|
|
142
|
-
# - submit_payment
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
# ---------- Connectors (optional) --------------------------------------------
|
|
146
|
-
# Connectors are pre-built tool bundles for common services.
|
|
147
|
-
# Set them up in code, not in this file:
|
|
148
|
-
#
|
|
149
|
-
# from roscoe.connectors import GitHubConnector
|
|
150
|
-
# gh = GitHubConnector({"token": "ghp_..."})
|
|
151
|
-
# agent = AgentRunner.from_config("agent_config.yaml", tools=gh.tools)
|
|
152
|
-
#
|
|
153
|
-
# Available connectors:
|
|
154
|
-
# RESTConnector — any REST API (base_url + token)
|
|
155
|
-
# JiraConnector — Jira Cloud (email + api_token)
|
|
156
|
-
# ServiceNowConnector — ServiceNow (instance_url + user/pass)
|
|
157
|
-
# OutlookConnector — Microsoft Graph email/calendar
|
|
158
|
-
# SharePointConnector — Microsoft Graph files
|
|
159
|
-
# GitHubConnector — GitHub API (PAT)
|
|
160
|
-
# NotionConnector — Notion API (integration token)
|
|
161
|
-
# GoogleWorkspaceConnector — Gmail + Calendar + Tasks + Drive (service account)
|
|
162
|
-
# SnowflakeConnector — Snowflake SQL (pip install roscoe[snowflake])
|
|
163
|
-
#
|
|
164
|
-
# Mix connector tools with your own:
|
|
165
|
-
# tools = [my_tool] + gh.tools + jira.tools
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
# ---------- Monitoring (optional) --------------------------------------------
|
|
169
|
-
# After runs accumulate, use the CLI to view a dashboard:
|
|
170
|
-
# roscoe monitor --path logs/audit.jsonl
|
|
171
|
-
#
|
|
172
|
-
# Export to Prometheus Pushgateway or Azure Monitor in code:
|
|
173
|
-
# from roscoe.monitoring.exporters.prometheus import PrometheusPushgatewayExporter
|
|
174
|
-
# exporter = PrometheusPushgatewayExporter(gateway_url="http://localhost:9091")
|
|
175
|
-
# exporter.push(metrics)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
# ---------- Evals (optional) -------------------------------------------------
|
|
179
|
-
# Define test cases in evals/test_cases.json (already scaffolded).
|
|
180
|
-
# Run from CLI:
|
|
181
|
-
# roscoe eval --dataset evals/test_cases.json --config agent_config.yaml
|
|
182
|
-
#
|
|
183
|
-
# Add --judge to enable LLM-as-judge scoring (output quality + hallucination).
|
|
184
|
-
# Without --judge, only deterministic tool-usage scoring runs.
|
|
185
|
-
#
|
|
186
|
-
# Compare two runs for regressions:
|
|
187
|
-
# from roscoe.evals.regression import compare_runs
|
|
188
|
-
# diff = compare_runs(report_a, report_b)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|