roscoe 0.1.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-0.1.0/LICENSE +21 -0
- roscoe-0.1.0/PKG-INFO +477 -0
- roscoe-0.1.0/README.md +442 -0
- roscoe-0.1.0/pyproject.toml +67 -0
- roscoe-0.1.0/roscoe/__init__.py +8 -0
- roscoe-0.1.0/roscoe/approval/__init__.py +5 -0
- roscoe-0.1.0/roscoe/approval/gate.py +73 -0
- roscoe-0.1.0/roscoe/cli/__init__.py +5 -0
- roscoe-0.1.0/roscoe/cli/eval_command.py +79 -0
- roscoe-0.1.0/roscoe/cli/init_command.py +486 -0
- roscoe-0.1.0/roscoe/cli/main.py +25 -0
- roscoe-0.1.0/roscoe/cli/monitor_command.py +29 -0
- roscoe-0.1.0/roscoe/cli/scaffold/.env.example +43 -0
- roscoe-0.1.0/roscoe/cli/scaffold/agent_config.yaml +176 -0
- roscoe-0.1.0/roscoe/cli/scaffold/docs.md +659 -0
- roscoe-0.1.0/roscoe/cli/scaffold/evals/test_cases.json +37 -0
- roscoe-0.1.0/roscoe/cli/scaffold/main.py +42 -0
- roscoe-0.1.0/roscoe/cli/scaffold/prompts/system.txt +26 -0
- roscoe-0.1.0/roscoe/cli/scaffold/tools/my_tools.py +60 -0
- roscoe-0.1.0/roscoe/cli/wizard_gui.py +343 -0
- roscoe-0.1.0/roscoe/config/__init__.py +0 -0
- roscoe-0.1.0/roscoe/config/loader.py +76 -0
- roscoe-0.1.0/roscoe/connectors/__init__.py +30 -0
- roscoe-0.1.0/roscoe/connectors/_graph_base.py +63 -0
- roscoe-0.1.0/roscoe/connectors/base_connector.py +59 -0
- roscoe-0.1.0/roscoe/connectors/github.py +79 -0
- roscoe-0.1.0/roscoe/connectors/google_workspace.py +229 -0
- roscoe-0.1.0/roscoe/connectors/jira.py +103 -0
- roscoe-0.1.0/roscoe/connectors/notion.py +87 -0
- roscoe-0.1.0/roscoe/connectors/outlook.py +90 -0
- roscoe-0.1.0/roscoe/connectors/rest_api.py +71 -0
- roscoe-0.1.0/roscoe/connectors/servicenow.py +93 -0
- roscoe-0.1.0/roscoe/connectors/sharepoint.py +66 -0
- roscoe-0.1.0/roscoe/connectors/snowflake.py +93 -0
- roscoe-0.1.0/roscoe/core/__init__.py +8 -0
- roscoe-0.1.0/roscoe/core/agent_base.py +32 -0
- roscoe-0.1.0/roscoe/core/agent_result.py +37 -0
- roscoe-0.1.0/roscoe/core/agent_runner.py +375 -0
- roscoe-0.1.0/roscoe/core/executor.py +124 -0
- roscoe-0.1.0/roscoe/core/state.py +18 -0
- roscoe-0.1.0/roscoe/evals/__init__.py +34 -0
- roscoe-0.1.0/roscoe/evals/dataset.py +61 -0
- roscoe-0.1.0/roscoe/evals/eval_runner.py +86 -0
- roscoe-0.1.0/roscoe/evals/regression.py +54 -0
- roscoe-0.1.0/roscoe/evals/report.py +47 -0
- roscoe-0.1.0/roscoe/evals/scorers/__init__.py +15 -0
- roscoe-0.1.0/roscoe/evals/scorers/_judge.py +31 -0
- roscoe-0.1.0/roscoe/evals/scorers/base.py +43 -0
- roscoe-0.1.0/roscoe/evals/scorers/hallucination.py +44 -0
- roscoe-0.1.0/roscoe/evals/scorers/output_quality.py +42 -0
- roscoe-0.1.0/roscoe/evals/scorers/tool_usage.py +40 -0
- roscoe-0.1.0/roscoe/llm/__init__.py +7 -0
- roscoe-0.1.0/roscoe/llm/base_provider.py +33 -0
- roscoe-0.1.0/roscoe/llm/capability_map.py +53 -0
- roscoe-0.1.0/roscoe/llm/provider_factory.py +160 -0
- roscoe-0.1.0/roscoe/memory/__init__.py +7 -0
- roscoe-0.1.0/roscoe/memory/conversation.py +34 -0
- roscoe-0.1.0/roscoe/memory/knowledge.py +100 -0
- roscoe-0.1.0/roscoe/memory/persistent.py +60 -0
- roscoe-0.1.0/roscoe/middleware/__init__.py +18 -0
- roscoe-0.1.0/roscoe/middleware/audit_logger.py +85 -0
- roscoe-0.1.0/roscoe/middleware/cost_tracker.py +70 -0
- roscoe-0.1.0/roscoe/middleware/rate_limiter.py +67 -0
- roscoe-0.1.0/roscoe/middleware/retry.py +95 -0
- roscoe-0.1.0/roscoe/monitoring/__init__.py +28 -0
- roscoe-0.1.0/roscoe/monitoring/alerts.py +65 -0
- roscoe-0.1.0/roscoe/monitoring/dashboard.py +41 -0
- roscoe-0.1.0/roscoe/monitoring/exporters/__init__.py +5 -0
- roscoe-0.1.0/roscoe/monitoring/exporters/azure_monitor.py +52 -0
- roscoe-0.1.0/roscoe/monitoring/exporters/prometheus.py +70 -0
- roscoe-0.1.0/roscoe/monitoring/metrics.py +148 -0
- roscoe-0.1.0/roscoe/monitoring/notifier.py +64 -0
- roscoe-0.1.0/roscoe/templates/__init__.py +35 -0
- roscoe-0.1.0/roscoe/templates/exec_assistant_agent/__init__.py +0 -0
- roscoe-0.1.0/roscoe/templates/exec_assistant_agent/agent_config.yaml +34 -0
- roscoe-0.1.0/roscoe/templates/exec_assistant_agent/prompts/system.txt +16 -0
- roscoe-0.1.0/roscoe/templates/exec_assistant_agent/tools/__init__.py +0 -0
- roscoe-0.1.0/roscoe/templates/exec_assistant_agent/tools/exec_tools.py +16 -0
- roscoe-0.1.0/roscoe/templates/google_workspace_agent/agent_config.yaml +32 -0
- roscoe-0.1.0/roscoe/templates/google_workspace_agent/prompts/system.txt +16 -0
- roscoe-0.1.0/roscoe/templates/google_workspace_agent/tools/__init__.py +0 -0
- roscoe-0.1.0/roscoe/templates/google_workspace_agent/tools/gws_tools.py +16 -0
- roscoe-0.1.0/roscoe/templates/hr_agent/__init__.py +0 -0
- roscoe-0.1.0/roscoe/templates/hr_agent/agent_config.yaml +37 -0
- roscoe-0.1.0/roscoe/templates/hr_agent/prompts/system.txt +13 -0
- roscoe-0.1.0/roscoe/templates/hr_agent/tools/__init__.py +0 -0
- roscoe-0.1.0/roscoe/templates/hr_agent/tools/hr_tools.py +68 -0
- roscoe-0.1.0/roscoe/templates/it_support_agent/__init__.py +0 -0
- roscoe-0.1.0/roscoe/templates/it_support_agent/agent_config.yaml +32 -0
- roscoe-0.1.0/roscoe/templates/it_support_agent/prompts/system.txt +15 -0
- roscoe-0.1.0/roscoe/templates/it_support_agent/tools/__init__.py +0 -0
- roscoe-0.1.0/roscoe/templates/it_support_agent/tools/it_tools.py +66 -0
- roscoe-0.1.0/roscoe/templates/knowledge_base_agent/__init__.py +0 -0
- roscoe-0.1.0/roscoe/templates/knowledge_base_agent/agent_config.yaml +34 -0
- roscoe-0.1.0/roscoe/templates/knowledge_base_agent/prompts/system.txt +11 -0
- roscoe-0.1.0/roscoe/templates/knowledge_base_agent/tools/__init__.py +0 -0
- roscoe-0.1.0/roscoe/templates/knowledge_base_agent/tools/kb_tools.py +49 -0
- roscoe-0.1.0/roscoe/templates/legal_agent/__init__.py +0 -0
- roscoe-0.1.0/roscoe/templates/legal_agent/agent_config.yaml +26 -0
- roscoe-0.1.0/roscoe/templates/legal_agent/prompts/system.txt +12 -0
- roscoe-0.1.0/roscoe/templates/legal_agent/tools/__init__.py +0 -0
- roscoe-0.1.0/roscoe/templates/legal_agent/tools/legal_tools.py +68 -0
- roscoe-0.1.0/roscoe/tools/__init__.py +5 -0
- roscoe-0.1.0/roscoe/tools/decorator.py +59 -0
- roscoe-0.1.0/roscoe.egg-info/PKG-INFO +477 -0
- roscoe-0.1.0/roscoe.egg-info/SOURCES.txt +110 -0
- roscoe-0.1.0/roscoe.egg-info/dependency_links.txt +1 -0
- roscoe-0.1.0/roscoe.egg-info/entry_points.txt +2 -0
- roscoe-0.1.0/roscoe.egg-info/requires.txt +19 -0
- roscoe-0.1.0/roscoe.egg-info/top_level.txt +1 -0
- roscoe-0.1.0/setup.cfg +4 -0
- roscoe-0.1.0/tests/test_smoke.py +14 -0
roscoe-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rhealaloo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
roscoe-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: roscoe
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Provider-agnostic LangChain agent framework with middleware and evals.
|
|
5
|
+
Author-email: rhealaloo <rhealaloo@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/rhealaloo45/roscoe
|
|
8
|
+
Project-URL: Repository, https://github.com/rhealaloo45/roscoe
|
|
9
|
+
Project-URL: Issues, https://github.com/rhealaloo45/roscoe/issues
|
|
10
|
+
Keywords: langchain,agents,llm,sdk,react
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: langchain==0.3.25
|
|
19
|
+
Requires-Dist: langchain-openai<0.4,>=0.2
|
|
20
|
+
Requires-Dist: langchain-google-genai<3.0,>=2.0
|
|
21
|
+
Requires-Dist: langchain-anthropic<0.4,>=0.3
|
|
22
|
+
Requires-Dist: langchain-ollama<0.4,>=0.2
|
|
23
|
+
Requires-Dist: pydantic<3.0,>=2.10
|
|
24
|
+
Requires-Dist: pyyaml<7.0,>=6.0
|
|
25
|
+
Requires-Dist: click<9.0,>=8.1
|
|
26
|
+
Requires-Dist: httpx<1.0,>=0.27
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest<9.0,>=8.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-asyncio<1.0,>=0.24; extra == "dev"
|
|
30
|
+
Provides-Extra: snowflake
|
|
31
|
+
Requires-Dist: snowflake-connector-python<4.0,>=3.0; extra == "snowflake"
|
|
32
|
+
Provides-Extra: azure
|
|
33
|
+
Requires-Dist: azure-monitor-opentelemetry<2.0,>=1.0; extra == "azure"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# roscoe
|
|
37
|
+
|
|
38
|
+
> Provider-agnostic AI agent SDK — production-ready from line one.
|
|
39
|
+
|
|
40
|
+
[](./LICENSE)
|
|
41
|
+
[](https://www.python.org)
|
|
42
|
+
|
|
43
|
+
**roscoe** is a Python SDK for building LLM-powered agents that ship with production
|
|
44
|
+
plumbing built in — retries, cost tracking, audit logging, rate limiting, human approval,
|
|
45
|
+
memory, monitoring, and evals. You write tools (plain Python functions) and a YAML config;
|
|
46
|
+
roscoe handles everything else.
|
|
47
|
+
|
|
48
|
+
Switch LLM providers by editing one config block. Your code never changes.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
pip install roscoe
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Quick start
|
|
57
|
+
|
|
58
|
+
### Option A: Scaffold with the CLI
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
roscoe init my-agent
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
A GUI wizard opens — pick your provider, toggle middleware, configure memory. Hit
|
|
65
|
+
**Create Project** and you get a ready-to-run folder:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
my-agent/
|
|
69
|
+
├── agent_config.yaml # fully commented — every option explained
|
|
70
|
+
├── main.py # 6 lines to run your agent
|
|
71
|
+
├── tools/my_tools.py # your @tool functions go here
|
|
72
|
+
├── prompts/system.txt # agent personality + instructions
|
|
73
|
+
├── evals/test_cases.json
|
|
74
|
+
└── .env.example # all possible credential placeholders
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
cd my-agent
|
|
79
|
+
cp .env.example .env # fill in your API key
|
|
80
|
+
python main.py
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Use `--quick` to skip the wizard, or `--cli` for a terminal-based wizard.
|
|
84
|
+
|
|
85
|
+
### Option B: Code it directly
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from roscoe import AgentRunner
|
|
89
|
+
from roscoe.tools import tool
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@tool
|
|
93
|
+
def get_price(sku: str) -> dict:
|
|
94
|
+
"""Fetch the price for a product SKU."""
|
|
95
|
+
return {"sku": sku, "price": 1999}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
agent = AgentRunner.from_config("agent_config.yaml", tools=[get_price])
|
|
99
|
+
result = agent.run("What is the price of SKU-001?")
|
|
100
|
+
|
|
101
|
+
print(result.output) # the LLM's answer
|
|
102
|
+
print(result.status) # "success" | "error" | "paused"
|
|
103
|
+
print(result.cost_usd) # estimated cost in USD
|
|
104
|
+
print(result.total_tokens) # token usage
|
|
105
|
+
print(result.run_id) # UUID tying this run to the audit trail
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Features
|
|
111
|
+
|
|
112
|
+
### Provider-agnostic
|
|
113
|
+
|
|
114
|
+
Swap LLM providers by editing the `model:` block in your YAML config. Your Python code
|
|
115
|
+
stays identical.
|
|
116
|
+
|
|
117
|
+
| Provider | Config key | Example model |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| OpenAI | `openai` | `gpt-4o-mini` |
|
|
120
|
+
| OpenRouter | `openai` + `base_url` | any of 100+ models |
|
|
121
|
+
| Azure OpenAI | `azure_openai` | `gpt-4o` (via deployment) |
|
|
122
|
+
| Anthropic | `anthropic` | `claude-sonnet-4-5` |
|
|
123
|
+
| Google Gemini | `gemini` | `gemini-1.5-pro` |
|
|
124
|
+
| Ollama (local) | `ollama` | `llama3.1` (free, no key) |
|
|
125
|
+
|
|
126
|
+
Register custom providers: `ProviderFactory.register("my_provider", MyProviderClass)`.
|
|
127
|
+
|
|
128
|
+
```yaml
|
|
129
|
+
# Just change this block — nothing else
|
|
130
|
+
model:
|
|
131
|
+
provider: openai
|
|
132
|
+
model: gpt-4o-mini
|
|
133
|
+
api_key: ${OPENAI_API_KEY}
|
|
134
|
+
temperature: 0.1
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Automatic middleware
|
|
138
|
+
|
|
139
|
+
All middleware is configured in YAML and runs automatically on every `agent.run()` call.
|
|
140
|
+
Zero boilerplate code.
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
middleware:
|
|
144
|
+
retry:
|
|
145
|
+
max_attempts: 3 # exponential backoff on transient errors
|
|
146
|
+
rate_limiter:
|
|
147
|
+
enabled: true
|
|
148
|
+
requests_per_minute: 60 # token-bucket per provider
|
|
149
|
+
cost_tracking:
|
|
150
|
+
enabled: true # USD estimate in result.cost_usd
|
|
151
|
+
audit:
|
|
152
|
+
enabled: true # JSONL log at logs/audit.jsonl
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
- **Retry** — handles rate limits, timeouts, 500s with exponential backoff.
|
|
156
|
+
- **Rate limiting** — token-bucket algorithm, one bucket per provider. Prevents thrashing
|
|
157
|
+
API limits when multiple agents share a provider. Ollama is skipped (no external limit).
|
|
158
|
+
- **Cost tracking** — reads `usage_metadata` from LangChain, prices via built-in rate
|
|
159
|
+
table. Extensible:
|
|
160
|
+
```python
|
|
161
|
+
from roscoe.middleware.cost_tracker import COST_TABLE
|
|
162
|
+
COST_TABLE["openai"]["gpt-4.1"] = {"input": 0.002, "output": 0.008}
|
|
163
|
+
```
|
|
164
|
+
- **Audit logging** — non-blocking JSONL, one line per run: run_id, agent, provider,
|
|
165
|
+
model, tokens, cost, status, latency. Feed to `roscoe monitor` for dashboards.
|
|
166
|
+
|
|
167
|
+
### Memory
|
|
168
|
+
|
|
169
|
+
Three types, all configured in YAML:
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
memory:
|
|
173
|
+
conversation:
|
|
174
|
+
enabled: true
|
|
175
|
+
window_size: 10 # last N messages per session_id
|
|
176
|
+
persistent:
|
|
177
|
+
enabled: true
|
|
178
|
+
backend: sqlite
|
|
179
|
+
connection: ./facts.db # long-term facts per user_id
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
- **Conversation** — short-term, per `session_id`, windowed. Pass `session_id` to
|
|
183
|
+
`agent.run()` to keep context across turns.
|
|
184
|
+
- **Persistent** — long-term facts per `user_id` in sqlite. The agent remembers "My name
|
|
185
|
+
is Rhea" across sessions.
|
|
186
|
+
- **Knowledge / RAG** — vector retrieval via FAISS (if installed) or a zero-dependency
|
|
187
|
+
keyword retriever. Set up in code:
|
|
188
|
+
```python
|
|
189
|
+
from roscoe.memory.knowledge import KnowledgeMemory
|
|
190
|
+
km = KnowledgeMemory.from_texts(["policy doc text..."], metadatas=[{"source": "hr.pdf"}])
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Connectors
|
|
194
|
+
|
|
195
|
+
Pre-built tool bundles for enterprise systems. Each connector gives you LangChain tools
|
|
196
|
+
you hand straight to `AgentRunner`:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from roscoe.connectors import GitHubConnector
|
|
200
|
+
|
|
201
|
+
gh = GitHubConnector({"token": "ghp_..."})
|
|
202
|
+
agent = AgentRunner.from_config("agent.yaml", tools=gh.tools)
|
|
203
|
+
|
|
204
|
+
# Mix with your own tools:
|
|
205
|
+
agent = AgentRunner.from_config("agent.yaml", tools=[my_tool] + gh.tools)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
| Connector | Tools | Auth |
|
|
209
|
+
|---|---|---|
|
|
210
|
+
| **REST** (any API) | GET, POST, PUT, DELETE | Bearer / Basic / API key |
|
|
211
|
+
| **Jira** | search, create, update, transition issues | Email + API token |
|
|
212
|
+
| **ServiceNow** | create, query, update incidents | Username + password |
|
|
213
|
+
| **Outlook** | send email, read inbox, create event, availability | MS Graph (OAuth2) |
|
|
214
|
+
| **SharePoint** | list files, download, upload, search | MS Graph (OAuth2) |
|
|
215
|
+
| **GitHub** | list repos, issues, PRs, create issue | Personal access token |
|
|
216
|
+
| **Notion** | search, pages, databases, blocks | Integration token |
|
|
217
|
+
| **Google Workspace** | Gmail send/read, Calendar, Tasks, Drive search | Service account |
|
|
218
|
+
| **Snowflake** | execute SQL queries | `pip install roscoe[snowflake]` |
|
|
219
|
+
|
|
220
|
+
### Human-in-the-loop
|
|
221
|
+
|
|
222
|
+
Make sensitive tools require approval before they run:
|
|
223
|
+
|
|
224
|
+
```yaml
|
|
225
|
+
middleware:
|
|
226
|
+
human_approval:
|
|
227
|
+
require_approval_for: ["send_email", "delete_record"]
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
result = agent.run("Send an email to bob@acme.com")
|
|
232
|
+
|
|
233
|
+
if result.status == "paused":
|
|
234
|
+
print(result.pending_action) # inspect what the agent wants to do
|
|
235
|
+
|
|
236
|
+
# approve — tool runs as planned
|
|
237
|
+
result = agent.resume(result.run_id, "approve")
|
|
238
|
+
|
|
239
|
+
# reject — tool is blocked, agent gets a rejection message
|
|
240
|
+
result = agent.resume(result.run_id, "reject")
|
|
241
|
+
|
|
242
|
+
# modify — change the arguments before running
|
|
243
|
+
result = agent.resume(result.run_id, "modify", payload={"to": "correct@acme.com"})
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The run stops *before* the gated tool executes and returns `status="paused"`. Call
|
|
247
|
+
`resume()` to continue. Wire this to a Slack button, a web UI, or a CLI prompt.
|
|
248
|
+
|
|
249
|
+
### Monitoring
|
|
250
|
+
|
|
251
|
+
Offline aggregation of your audit logs — no live server required.
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
roscoe monitor --path logs/audit.jsonl
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Outputs a text dashboard with:
|
|
258
|
+
- Total runs, cost per day, cost per agent
|
|
259
|
+
- Latency percentiles (p50 / p95 / p99) per agent
|
|
260
|
+
- Error rate breakdown
|
|
261
|
+
- Token usage summary
|
|
262
|
+
|
|
263
|
+
**Alerts** — configure thresholds for daily cost, error rate, and latency:
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from roscoe.monitoring.alerts import check_and_notify
|
|
267
|
+
from roscoe.monitoring.notifier import build_notifier
|
|
268
|
+
|
|
269
|
+
notifier = build_notifier("slack", {"webhook_url": "https://hooks.slack.com/..."})
|
|
270
|
+
check_and_notify(metrics, alert_config, notifier)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Exporters** — push metrics to Prometheus Pushgateway or Azure Monitor:
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
from roscoe.monitoring.exporters.prometheus import PrometheusPushgatewayExporter
|
|
277
|
+
exporter = PrometheusPushgatewayExporter(gateway_url="http://localhost:9091")
|
|
278
|
+
exporter.push(metrics)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Evals
|
|
282
|
+
|
|
283
|
+
Test your agent with a dataset of cases and score the results:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
roscoe eval --dataset evals/test_cases.json --config agent_config.yaml
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Scorers:**
|
|
290
|
+
- **Tool usage** — deterministic, order-aware (did the agent call the right tools?).
|
|
291
|
+
- **Output quality** — LLM-as-judge, 0–10 scale. Add `--judge` to enable.
|
|
292
|
+
- **Hallucination** — LLM-as-judge, checks output against provided context docs.
|
|
293
|
+
|
|
294
|
+
**Regression diffing** — compare two eval runs:
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
from roscoe.evals.regression import compare_runs
|
|
298
|
+
diff = compare_runs(report_a, report_b)
|
|
299
|
+
print(diff.improved) # cases that got better
|
|
300
|
+
print(diff.regressed) # cases that got worse
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Test case format** (`evals/test_cases.json`):
|
|
304
|
+
|
|
305
|
+
```json
|
|
306
|
+
{
|
|
307
|
+
"cases": [
|
|
308
|
+
{
|
|
309
|
+
"id": "weather-london",
|
|
310
|
+
"input": "What's the weather in London?",
|
|
311
|
+
"expected_tools": ["get_weather"],
|
|
312
|
+
"expected_output": "Should mention London weather",
|
|
313
|
+
"context_docs": ["London is currently 15°C and rainy."]
|
|
314
|
+
}
|
|
315
|
+
]
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Templates
|
|
320
|
+
|
|
321
|
+
Six pre-built templates, each with tools, system prompt, config, and approval gates
|
|
322
|
+
pre-configured:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
roscoe init my-hr-bot --template hr_agent
|
|
326
|
+
roscoe init my-it-bot --template it_support_agent
|
|
327
|
+
roscoe init my-legal --template legal_agent
|
|
328
|
+
roscoe init my-kb --template knowledge_base_agent
|
|
329
|
+
roscoe init my-ea --template exec_assistant_agent
|
|
330
|
+
roscoe init my-gws --template google_workspace_agent
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
| Template | Use case | Connector | Approval gate |
|
|
334
|
+
|---|---|---|---|
|
|
335
|
+
| `hr_agent` | Leave, payslips, personal details | REST API | submit_leave_request |
|
|
336
|
+
| `it_support_agent` | Tickets, escalation, KB search | ServiceNow | escalate_ticket |
|
|
337
|
+
| `legal_agent` | Contract search, clause extraction, risk flags | Knowledge (RAG) | — |
|
|
338
|
+
| `knowledge_base_agent` | Q&A over Notion / SharePoint / docs | Notion + Knowledge | — (read-only) |
|
|
339
|
+
| `exec_assistant_agent` | Email, calendar, availability | Outlook (MS Graph) | send_email, create_event |
|
|
340
|
+
| `google_workspace_agent` | Gmail, Calendar, Tasks, Drive | Google Workspace | send_email, create_event, create_task |
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Architecture
|
|
345
|
+
|
|
346
|
+
roscoe runs its own async ReAct loop (no LangGraph dependency). The loop is ~100 lines
|
|
347
|
+
in `roscoe/core/executor.py`:
|
|
348
|
+
|
|
349
|
+
```
|
|
350
|
+
User message
|
|
351
|
+
→ model.invoke(messages)
|
|
352
|
+
→ tool_calls in response?
|
|
353
|
+
→ approval gate check (pause if gated)
|
|
354
|
+
→ execute tools
|
|
355
|
+
→ append results to messages
|
|
356
|
+
→ loop back to model
|
|
357
|
+
→ no tool_calls? done → AgentResult
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Built on **LangChain** (models, tools, messages) but not LangGraph. This keeps the agent
|
|
361
|
+
loop small, transparent, and easy to debug.
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## CLI reference
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
roscoe init <name> # scaffold with GUI wizard
|
|
369
|
+
roscoe init <name> --quick # scaffold with defaults (no wizard)
|
|
370
|
+
roscoe init <name> --cli # scaffold with terminal wizard
|
|
371
|
+
roscoe init <name> --template <t> # scaffold from a template
|
|
372
|
+
|
|
373
|
+
roscoe monitor # dashboard from logs/audit.jsonl
|
|
374
|
+
roscoe monitor --path /path/to/audit.jsonl # custom audit log path
|
|
375
|
+
|
|
376
|
+
roscoe eval --dataset cases.json --config agent.yaml # tool-usage scoring
|
|
377
|
+
roscoe eval --dataset cases.json --config agent.yaml --judge # + LLM-as-judge
|
|
378
|
+
roscoe eval --dataset cases.json --config agent.yaml --tools module:attr # custom tools
|
|
379
|
+
|
|
380
|
+
roscoe --version # print version
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Install
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
pip install roscoe # core
|
|
389
|
+
pip install "roscoe[snowflake]" # + Snowflake driver
|
|
390
|
+
pip install "roscoe[azure]" # + Azure Monitor exporter
|
|
391
|
+
pip install "roscoe[dev]" # + pytest (for contributors)
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### From source
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
git clone https://github.com/rhealaloo45/roscoe.git
|
|
398
|
+
cd roscoe
|
|
399
|
+
pip install -e ".[dev]"
|
|
400
|
+
pytest -q # 121 tests, all passing
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Writing tools
|
|
406
|
+
|
|
407
|
+
A tool is a plain Python function with type hints and a docstring:
|
|
408
|
+
|
|
409
|
+
```python
|
|
410
|
+
from roscoe.tools import tool
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
@tool
|
|
414
|
+
def search_docs(query: str) -> list[dict]:
|
|
415
|
+
"""Search internal documents. Use when the user asks about company policies."""
|
|
416
|
+
# your logic here
|
|
417
|
+
return [{"title": "Remote Work Policy", "snippet": "..."}]
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
- The **docstring** is what the LLM reads to decide when to call the tool.
|
|
421
|
+
- **Type hints** are used to generate the JSON schema automatically.
|
|
422
|
+
- **Return dicts or primitives** — the LLM reads the return value.
|
|
423
|
+
- Add the tool to the `TOOLS` list in `tools/my_tools.py`, or pass it directly to
|
|
424
|
+
`AgentRunner.from_config()`.
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Configuration reference
|
|
429
|
+
|
|
430
|
+
Full `agent_config.yaml` with all options (also generated by `roscoe init` with inline
|
|
431
|
+
comments):
|
|
432
|
+
|
|
433
|
+
```yaml
|
|
434
|
+
agent_name: my-agent
|
|
435
|
+
|
|
436
|
+
system_prompt_file: prompts/system.txt
|
|
437
|
+
# system_prompt: |
|
|
438
|
+
# Inline prompt alternative
|
|
439
|
+
|
|
440
|
+
model:
|
|
441
|
+
provider: openai # openai | azure_openai | anthropic | gemini | ollama
|
|
442
|
+
model: gpt-4o-mini
|
|
443
|
+
api_key: ${OPENAI_API_KEY} # resolved from environment
|
|
444
|
+
temperature: 0.1
|
|
445
|
+
# base_url: https://openrouter.ai/api/v1 # for OpenRouter / custom endpoints
|
|
446
|
+
# max_tokens: 4096
|
|
447
|
+
|
|
448
|
+
memory:
|
|
449
|
+
conversation:
|
|
450
|
+
enabled: true
|
|
451
|
+
window_size: 10
|
|
452
|
+
persistent:
|
|
453
|
+
enabled: false
|
|
454
|
+
backend: sqlite
|
|
455
|
+
connection: ./facts.db
|
|
456
|
+
|
|
457
|
+
middleware:
|
|
458
|
+
retry:
|
|
459
|
+
max_attempts: 3
|
|
460
|
+
rate_limiter:
|
|
461
|
+
enabled: true
|
|
462
|
+
requests_per_minute: 60
|
|
463
|
+
cost_tracking:
|
|
464
|
+
enabled: true
|
|
465
|
+
audit:
|
|
466
|
+
enabled: true
|
|
467
|
+
# human_approval:
|
|
468
|
+
# require_approval_for: ["send_email", "delete_record"]
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Environment variables use `${VAR_NAME}` syntax and are resolved at config load time.
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## License
|
|
476
|
+
|
|
477
|
+
MIT — see [LICENSE](./LICENSE).
|