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.
Files changed (124) hide show
  1. {roscoe-2.0.0/roscoe.egg-info → roscoe-2.2.0}/PKG-INFO +1 -1
  2. {roscoe-2.0.0 → roscoe-2.2.0}/pyproject.toml +1 -1
  3. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/__init__.py +1 -1
  4. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/init_command.py +5 -5
  5. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/main.py +6 -1
  6. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/run_web.py +101 -77
  7. roscoe-2.2.0/roscoe/cli/scaffold/agent_config.yaml +36 -0
  8. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/docs.md +86 -19
  9. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/wizard_gui.py +3 -0
  10. {roscoe-2.0.0 → roscoe-2.2.0/roscoe.egg-info}/PKG-INFO +1 -1
  11. roscoe-2.0.0/roscoe/cli/scaffold/agent_config.yaml +0 -188
  12. {roscoe-2.0.0 → roscoe-2.2.0}/LICENSE +0 -0
  13. {roscoe-2.0.0 → roscoe-2.2.0}/README.md +0 -0
  14. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/approval/__init__.py +0 -0
  15. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/approval/gate.py +0 -0
  16. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/__init__.py +0 -0
  17. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/eval_command.py +0 -0
  18. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/eval_gui.py +0 -0
  19. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/google_auth_command.py +0 -0
  20. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/gui_theme.py +0 -0
  21. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/monitor_command.py +0 -0
  22. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/monitor_gui.py +0 -0
  23. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/pricing_command.py +0 -0
  24. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/pricing_gui.py +0 -0
  25. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/run_command.py +0 -0
  26. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/.env.example +0 -0
  27. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/db.py +0 -0
  28. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/evals/test_cases.json +0 -0
  29. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/main.py +0 -0
  30. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/prompts/system.txt +0 -0
  31. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/cli/scaffold/tools/my_tools.py +0 -0
  32. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/config/__init__.py +0 -0
  33. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/config/loader.py +0 -0
  34. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/__init__.py +0 -0
  35. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/_graph_base.py +0 -0
  36. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/base_connector.py +0 -0
  37. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/github.py +0 -0
  38. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/google_workspace.py +0 -0
  39. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/jira.py +0 -0
  40. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/notion.py +0 -0
  41. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/outlook.py +0 -0
  42. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/rest_api.py +0 -0
  43. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/servicenow.py +0 -0
  44. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/sharepoint.py +0 -0
  45. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/connectors/snowflake.py +0 -0
  46. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/core/__init__.py +0 -0
  47. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/core/agent_base.py +0 -0
  48. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/core/agent_result.py +0 -0
  49. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/core/agent_runner.py +0 -0
  50. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/core/executor.py +0 -0
  51. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/core/state.py +0 -0
  52. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/__init__.py +0 -0
  53. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/dataset.py +0 -0
  54. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/eval_runner.py +0 -0
  55. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/regression.py +0 -0
  56. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/report.py +0 -0
  57. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/scorers/__init__.py +0 -0
  58. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/scorers/_judge.py +0 -0
  59. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/scorers/base.py +0 -0
  60. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/scorers/hallucination.py +0 -0
  61. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/scorers/output_quality.py +0 -0
  62. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/evals/scorers/tool_usage.py +0 -0
  63. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/llm/__init__.py +0 -0
  64. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/llm/base_provider.py +0 -0
  65. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/llm/capability_map.py +0 -0
  66. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/llm/provider_factory.py +0 -0
  67. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/memory/__init__.py +0 -0
  68. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/memory/conversation.py +0 -0
  69. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/memory/knowledge.py +0 -0
  70. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/memory/persistent.py +0 -0
  71. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/middleware/__init__.py +0 -0
  72. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/middleware/audit_logger.py +0 -0
  73. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/middleware/cost_tracker.py +0 -0
  74. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/middleware/rate_limiter.py +0 -0
  75. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/middleware/retry.py +0 -0
  76. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/__init__.py +0 -0
  77. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/alerts.py +0 -0
  78. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/dashboard.py +0 -0
  79. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/exporters/__init__.py +0 -0
  80. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/exporters/azure_monitor.py +0 -0
  81. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/exporters/prometheus.py +0 -0
  82. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/metrics.py +0 -0
  83. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/notifier.py +0 -0
  84. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/monitoring/web.py +0 -0
  85. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/pricing.py +0 -0
  86. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/__init__.py +0 -0
  87. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/exec_assistant_agent/__init__.py +0 -0
  88. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/exec_assistant_agent/agent_config.yaml +0 -0
  89. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/exec_assistant_agent/prompts/system.txt +0 -0
  90. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/exec_assistant_agent/tools/__init__.py +0 -0
  91. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/exec_assistant_agent/tools/exec_tools.py +0 -0
  92. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/google_workspace_agent/agent_config.yaml +0 -0
  93. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/google_workspace_agent/prompts/system.txt +0 -0
  94. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/google_workspace_agent/tools/__init__.py +0 -0
  95. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/google_workspace_agent/tools/gws_tools.py +0 -0
  96. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/hr_agent/__init__.py +0 -0
  97. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/hr_agent/agent_config.yaml +0 -0
  98. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/hr_agent/prompts/system.txt +0 -0
  99. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/hr_agent/tools/__init__.py +0 -0
  100. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/hr_agent/tools/hr_tools.py +0 -0
  101. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/it_support_agent/__init__.py +0 -0
  102. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/it_support_agent/agent_config.yaml +0 -0
  103. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/it_support_agent/prompts/system.txt +0 -0
  104. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/it_support_agent/tools/__init__.py +0 -0
  105. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/it_support_agent/tools/it_tools.py +0 -0
  106. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/knowledge_base_agent/__init__.py +0 -0
  107. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/knowledge_base_agent/agent_config.yaml +0 -0
  108. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/knowledge_base_agent/prompts/system.txt +0 -0
  109. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/knowledge_base_agent/tools/__init__.py +0 -0
  110. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/knowledge_base_agent/tools/kb_tools.py +0 -0
  111. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/legal_agent/__init__.py +0 -0
  112. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/legal_agent/agent_config.yaml +0 -0
  113. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/legal_agent/prompts/system.txt +0 -0
  114. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/legal_agent/tools/__init__.py +0 -0
  115. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/templates/legal_agent/tools/legal_tools.py +0 -0
  116. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/tools/__init__.py +0 -0
  117. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe/tools/decorator.py +0 -0
  118. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe.egg-info/SOURCES.txt +0 -0
  119. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe.egg-info/dependency_links.txt +0 -0
  120. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe.egg-info/entry_points.txt +0 -0
  121. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe.egg-info/requires.txt +0 -0
  122. {roscoe-2.0.0 → roscoe-2.2.0}/roscoe.egg-info/top_level.txt +0 -0
  123. {roscoe-2.0.0 → roscoe-2.2.0}/setup.cfg +0 -0
  124. {roscoe-2.0.0 → roscoe-2.2.0}/tests/test_smoke.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roscoe
3
- Version: 2.0.0
3
+ Version: 2.2.0
4
4
  Summary: roscoe — Ready-to-run Orchestration SDK: Configurable, Observable, Extensible.
5
5
  Author-email: rhealaloo <rhealaloo@gmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "roscoe"
7
- version = "2.0.0"
7
+ version = "2.2.0"
8
8
  description = "roscoe — Ready-to-run Orchestration SDK: Configurable, Observable, Extensible."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -3,6 +3,6 @@
3
3
  from roscoe.core.agent_result import AgentResult
4
4
  from roscoe.core.agent_runner import AgentRunner
5
5
 
6
- __version__ = "2.0.0"
6
+ __version__ = "2.2.0"
7
7
 
8
8
  __all__ = ["AgentRunner", "AgentResult", "__version__"]
@@ -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:\n"
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 # flip to true to enable cross-session memory",
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
- return {"type": "final", "output": result.output,
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:#0b0d12;color:#e2e8f0;height:100vh;overflow:hidden}
128
+ background:#f7f8fa;color:#1e293b;height:100vh;overflow:hidden}
125
129
 
126
- /* landing page behind the widget */
127
- .landing{height:100vh;display:flex;align-items:center;justify-content:center;
128
- background:radial-gradient(circle at 30% 20%,#182338,#0b0d12 60%)}
129
- .card{width:380px;max-width:90vw;background:#161923;border:1px solid #262b38;border-radius:16px;padding:32px}
130
- .card h1{font-size:20px;font-weight:700;margin-bottom:6px}
131
- .card p{font-size:13px;color:#8b93a3;margin-bottom:24px}
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:#8b93a3;margin-bottom:6px}
134
- .field input{width:100%;padding:10px 13px;background:#0f1117;border:1px solid #334155;
135
- border-radius:10px;color:#e2e8f0;font-size:14px;outline:none}
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
- .card button{width:100%;margin-top:8px;padding:11px;background:#2563eb;color:#fff;border:none;
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
- .card button:hover{background:#1d4ed8}
140
- .signedin{display:none;text-align:center;font-size:13px;color:#8b93a3}
141
- .signedin b{color:#e2e8f0}
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
- /* floating launcher */
144
- .launcher{position:fixed;right:24px;bottom:24px;width:58px;height:58px;border-radius:50%;
145
- background:#2563eb;display:flex;align-items:center;justify-content:center;cursor:pointer;
146
- box-shadow:0 6px 20px rgba(37,99,235,.45);z-index:20;transition:transform .15s}
147
- .launcher:hover{transform:scale(1.06)}
148
- .launcher svg{width:26px;height:26px;fill:#fff}
149
- .dot{position:absolute;top:2px;right:2px;width:12px;height:12px;background:#22c55e;border:2px solid #0b0d12;border-radius:50%}
150
-
151
- /* chat panel */
152
- .panel{position:fixed;right:24px;bottom:96px;width:360px;height:520px;max-height:76vh;
153
- background:#161923;border:1px solid #262b38;border-radius:16px;display:flex;flex-direction:column;
154
- overflow:hidden;box-shadow:0 20px 50px rgba(0,0,0,.5);z-index:21;
155
- opacity:0;transform:translateY(16px) scale(.98);pointer-events:none;transition:opacity .16s,transform .16s}
156
- .panel.open{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}
157
- .phead{background:linear-gradient(135deg,#1e3a5f,#0f172a);padding:14px 16px;display:flex;align-items:center;justify-content:space-between}
158
- .phead .t{font-size:14px;font-weight:600}
159
- .phead .s{font-size:10.5px;opacity:.65;margin-top:1px}
160
- .pclose{cursor:pointer;opacity:.7;font-size:18px;line-height:1;padding:2px 4px}
161
- .pclose:hover{opacity:1}
162
- .msgs{flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:10px}
163
- .msgs::-webkit-scrollbar{width:5px}.msgs::-webkit-scrollbar-thumb{background:#334155;border-radius:3px}
164
- .m{max-width:84%;padding:9px 13px;border-radius:13px;font-size:13.5px;line-height:1.5;white-space:pre-wrap;word-wrap:break-word}
165
- .m.user{align-self:flex-end;background:#2563eb;color:#fff;border-bottom-right-radius:4px}
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:#3a2e07;border:1px solid #a16207;border-radius:12px;padding:11px 14px;font-size:12.5px}
174
- .approve b{color:#fbbf24}.approve .tc{font-family:ui-monospace,monospace;color:#fde68a;margin:5px 0;word-break:break-all}
175
- .approve button{border:none;border-radius:8px;padding:6px 14px;font-weight:600;cursor:pointer;margin-right:8px;margin-top:6px;font-size:12px}
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:6px 14px;font-size:10.5px;color:#64748b;border-top:1px solid #262b38}
178
- .in{display:flex;gap:8px;padding:10px 12px;border-top:1px solid #262b38}
179
- .in input{flex:1;padding:9px 13px;background:#0f1117;border:1px solid #334155;border-radius:10px;color:#e2e8f0;font-size:13.5px;outline:none}
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:9px 16px;background:#2563eb;color:#fff;border:none;border-radius:10px;font-weight:600;cursor:pointer;font-size:13px}
182
- .in button:disabled{background:#334155;cursor:not-allowed}
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="landing">
186
- <div class="card">
198
+ <div class="layout">
199
+ <div class="sidebar">
187
200
  <h1>Welcome</h1>
188
- <p>Sign in to get personalized help, or just open the chat to ask a question.</p>
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>. Use the chat button to start.</div>
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="panel" id="panel">
204
- <div class="phead">
205
- <div><div class="t">roscoe run</div><div class="s" id="meta">agent chat</div></div>
206
- <div class="pclose" onclick="togglePanel()">&times;</div>
207
- </div>
208
- <div class="msgs" id="msgs"><div class="m bot">Hi — I'm your agent. Ask me anything.</div></div>
209
- <div class="bar" id="bar">ready</div>
210
- <div class="in">
211
- <input id="q" placeholder="Type a message…" onkeydown="if(event.key==='Enter')send()">
212
- <button id="btn" onclick="send()">Send</button>
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
- const panel=document.getElementById('panel');
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
- if(!opened)togglePanel();
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){const d=document.createElement('div');d.className='m '+cls;d.textContent=txt;msgs.appendChild(d);msgs.scrollTop=msgs.scrollHeight;return d}
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>&#9208; approval required</b>';
259
- (ev.tool_calls||[]).forEach(tc=>{html+='<div class="tc">'+esc(tc.name)+'('+esc(JSON.stringify(tc.args||{}))+')</div>'});
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. [Writing tools](#writing-tools)
14
- 3. [Running the agent](#running-the-agent)
15
- 4. [Multi-turn conversations](#multi-turn-conversations)
16
- 5. [Swapping LLM providers](#swapping-llm-providers)
17
- 6. [Memory](#memory)
18
- 7. [Connectors](#connectors)
19
- 8. [Human-in-the-loop (HITL)](#human-in-the-loop-hitl)
20
- 9. [Audit log & cost tracking](#audit-log--cost-tracking)
21
- 10. [Monitoring dashboard](#monitoring-dashboard)
22
- 11. [Alerts & exporters](#alerts--exporters)
23
- 12. [Evals](#evals)
24
- 13. [Extending the cost table](#extending-the-cost-table)
25
- 14. [Configuration reference](#configuration-reference)
26
- 15. [Async usage](#async-usage)
27
- 16. [Troubleshooting](#troubleshooting)
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
- **Single-shot:**
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. Add your own before calling `agent.run()`:
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roscoe
3
- Version: 2.0.0
3
+ Version: 2.2.0
4
4
  Summary: roscoe — Ready-to-run Orchestration SDK: Configurable, Observable, Extensible.
5
5
  Author-email: rhealaloo <rhealaloo@gmail.com>
6
6
  License-Expression: MIT
@@ -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