agentx-kit 0.5.0__tar.gz → 0.6.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.
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/PKG-INFO +48 -1
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/README.md +47 -0
- agentx_kit-0.6.0/examples/README.md +28 -0
- agentx_kit-0.6.0/examples/demo_local.sh +42 -0
- agentx_kit-0.6.0/examples/demo_mcp.py +78 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/pyproject.toml +1 -1
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/__init__.py +1 -1
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/connector/build.py +5 -4
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/generator.py +21 -5
- agentx_kit-0.6.0/src/agentx/scaffold/templates/README.md.j2 +69 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/evals/run_evals.py.j2 +3 -9
- agentx_kit-0.6.0/src/agentx/scaffold/templates/pkg/agents.py.j2 +21 -0
- agentx_kit-0.6.0/src/agentx/scaffold/templates/pkg/crew.py.j2 +18 -0
- agentx_kit-0.6.0/src/agentx/scaffold/templates/pkg/graph.py.j2 +69 -0
- agentx_kit-0.6.0/src/agentx/scaffold/templates/pkg/main.py.j2 +45 -0
- agentx_kit-0.6.0/src/agentx/scaffold/templates/pkg/nodes.py.j2 +76 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/pkg/server.py.j2 +9 -18
- agentx_kit-0.6.0/src/agentx/scaffold/templates/pkg/state.py.j2 +21 -0
- agentx_kit-0.6.0/src/agentx/scaffold/templates/pkg/tasks.py.j2 +18 -0
- agentx_kit-0.6.0/src/agentx/scaffold/templates/pkg/tools.py.j2 +26 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/tests/test_enterprise.py +2 -1
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/tests/test_scaffold.py +15 -5
- agentx_kit-0.5.0/src/agentx/scaffold/templates/README.md.j2 +0 -46
- agentx_kit-0.5.0/src/agentx/scaffold/templates/pkg/agents.py.j2 +0 -77
- agentx_kit-0.5.0/src/agentx/scaffold/templates/pkg/main.py.j2 +0 -87
- agentx_kit-0.5.0/src/agentx/scaffold/templates/pkg/tools.py.j2 +0 -16
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/.agentx/llm_cache.sqlite +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/.claude-plugin/marketplace.json +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/.github/workflows/publish.yml +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/.gitignore +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/DESIGN.md +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/LICENSE +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/RESEARCH.md +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/integrations/claude-plugin/.claude-plugin/plugin.json +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/integrations/claude-plugin/.mcp.json +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/integrations/claude-plugin/README.md +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/integrations/claude-plugin/commands/new-agent.md +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/integrations/vscode/.vscodeignore +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/integrations/vscode/README.md +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/integrations/vscode/extension.js +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/integrations/vscode/package.json +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/cache.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/cli.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/config.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/connector/__init__.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/connector/recommend.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/connector/server.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/dashboard/__init__.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/dashboard/app.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/frameworks/__init__.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/frameworks/crewai_agent.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/frameworks/langchain_agent.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/guardrails.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/insights/__init__.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/insights/analyze.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/insights/log.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/insights/optimize.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/insights/tokens.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/memory/__init__.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/memory/store.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/observability.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/prompts/__init__.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/prompts/templates.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/providers/__init__.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/providers/base.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/providers/factory.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/providers/registry.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/rag/__init__.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/rag/pipeline.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/reliability.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/__init__.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/prompts_store.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/spec.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/Dockerfile.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/ci.yml.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/docker-compose.yml.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/dockerignore.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/env.example.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/evals/dataset.json.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/gitignore.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/mcp_servers.json.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/pkg/__init__.py.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/pkg/config.py.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/pkg/guardrails.py.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/pkg/memory.py.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/pkg/observability.py.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/pkg/prompts.py.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/pkg/rag.py.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/pyproject.toml.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/templates/skills_seed.json.j2 +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/scaffold/wizard.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/skills/__init__.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/skills/registry.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/structured.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/tools/__init__.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/tools/builtin.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/src/agentx/tools/mcp.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/tests/test_cache.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/tests/test_connector.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/tests/test_insights.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/tests/test_prompts.py +0 -0
- {agentx_kit-0.5.0 → agentx_kit-0.6.0}/tests/test_providers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentx-kit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: An open-source, provider-agnostic agentic framework + interactive project scaffolder for LangChain and CrewAI. Pick your LLM provider, agents, RAG, memory, MCP tools and skills — generate a ready-to-run uv project.
|
|
5
5
|
Project-URL: Homepage, https://github.com/muhammadyahiya/agentx-kit
|
|
6
6
|
Project-URL: Repository, https://github.com/muhammadyahiya/agentx-kit
|
|
@@ -123,6 +123,53 @@ agentx new # interactive wizard → scaffolds a uv project
|
|
|
123
123
|
> The PyPI distribution is **`agentx-kit`**; the import name and CLI are **`agentx`**
|
|
124
124
|
> (`pip install agentx-kit` → `import agentx` / `agentx --help`).
|
|
125
125
|
|
|
126
|
+
## 🚀 60-second walkthrough
|
|
127
|
+
```bash
|
|
128
|
+
# 1. Install
|
|
129
|
+
pip install "agentx-kit[all]"
|
|
130
|
+
|
|
131
|
+
# 2. See what you can target
|
|
132
|
+
agentx providers # 9 LLM providers + the env vars each needs
|
|
133
|
+
|
|
134
|
+
# 3. Scaffold a complete project from one line (no keys needed to generate)
|
|
135
|
+
agentx new --yes --name my-bot \
|
|
136
|
+
--provider openai \
|
|
137
|
+
--prompt "You are a support agent that answers from our docs."
|
|
138
|
+
|
|
139
|
+
# 4. Run it
|
|
140
|
+
cd my-bot && cp .env.example .env # add your API key
|
|
141
|
+
uv sync && uv run my-bot
|
|
142
|
+
|
|
143
|
+
# 5. Tune prompts live (tokens, cost, quality, optimize) — optional UI
|
|
144
|
+
pip install "agentx-kit[dashboard]" && agentx dashboard
|
|
145
|
+
|
|
146
|
+
# 6. Use it from Claude / Copilot / Codex
|
|
147
|
+
claude mcp add agentx-kit -- agentx mcp
|
|
148
|
+
```
|
|
149
|
+
Prefer guided? Just run `agentx new` (interactive wizard) or
|
|
150
|
+
`agentx new --enterprise` for the full production stack
|
|
151
|
+
(tracing, guardrails, FastAPI, Docker, CI, evals, caching).
|
|
152
|
+
|
|
153
|
+
### 🧭 Command cheat-sheet
|
|
154
|
+
| Command | What it does |
|
|
155
|
+
|---|---|
|
|
156
|
+
| `agentx new` | Interactive wizard → scaffold a uv project |
|
|
157
|
+
| `agentx new --yes [opts]` | Non-interactive scaffold (`--enterprise` for the full pack) |
|
|
158
|
+
| `agentx providers` | List LLM providers + required env vars |
|
|
159
|
+
| `agentx prompt list/set/add/remove` | Manage an existing project's prompts (`-d` opens the dashboard) |
|
|
160
|
+
| `agentx dashboard` | Prompt observability + optimization UI (`[dashboard]` extra) |
|
|
161
|
+
| `agentx cache stats / clear` | Inspect/clear the LLM response cache |
|
|
162
|
+
| `agentx mcp` | Run as an MCP server for Claude/Copilot/Codex |
|
|
163
|
+
| `agentx mcp --print-config` | Print the client config for those tools |
|
|
164
|
+
| `agentx version` | Show the installed version |
|
|
165
|
+
|
|
166
|
+
### ▶️ Try the demos (no API keys needed)
|
|
167
|
+
```bash
|
|
168
|
+
bash examples/demo_local.sh # verify local setup end-to-end
|
|
169
|
+
python examples/demo_mcp.py # test the Claude/Copilot MCP path (real handshake)
|
|
170
|
+
```
|
|
171
|
+
See [`examples/`](examples/) for details.
|
|
172
|
+
|
|
126
173
|
## 📦 Installation
|
|
127
174
|
|
|
128
175
|
### From PyPI (recommended)
|
|
@@ -19,6 +19,53 @@ agentx new # interactive wizard → scaffolds a uv project
|
|
|
19
19
|
> The PyPI distribution is **`agentx-kit`**; the import name and CLI are **`agentx`**
|
|
20
20
|
> (`pip install agentx-kit` → `import agentx` / `agentx --help`).
|
|
21
21
|
|
|
22
|
+
## 🚀 60-second walkthrough
|
|
23
|
+
```bash
|
|
24
|
+
# 1. Install
|
|
25
|
+
pip install "agentx-kit[all]"
|
|
26
|
+
|
|
27
|
+
# 2. See what you can target
|
|
28
|
+
agentx providers # 9 LLM providers + the env vars each needs
|
|
29
|
+
|
|
30
|
+
# 3. Scaffold a complete project from one line (no keys needed to generate)
|
|
31
|
+
agentx new --yes --name my-bot \
|
|
32
|
+
--provider openai \
|
|
33
|
+
--prompt "You are a support agent that answers from our docs."
|
|
34
|
+
|
|
35
|
+
# 4. Run it
|
|
36
|
+
cd my-bot && cp .env.example .env # add your API key
|
|
37
|
+
uv sync && uv run my-bot
|
|
38
|
+
|
|
39
|
+
# 5. Tune prompts live (tokens, cost, quality, optimize) — optional UI
|
|
40
|
+
pip install "agentx-kit[dashboard]" && agentx dashboard
|
|
41
|
+
|
|
42
|
+
# 6. Use it from Claude / Copilot / Codex
|
|
43
|
+
claude mcp add agentx-kit -- agentx mcp
|
|
44
|
+
```
|
|
45
|
+
Prefer guided? Just run `agentx new` (interactive wizard) or
|
|
46
|
+
`agentx new --enterprise` for the full production stack
|
|
47
|
+
(tracing, guardrails, FastAPI, Docker, CI, evals, caching).
|
|
48
|
+
|
|
49
|
+
### 🧭 Command cheat-sheet
|
|
50
|
+
| Command | What it does |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `agentx new` | Interactive wizard → scaffold a uv project |
|
|
53
|
+
| `agentx new --yes [opts]` | Non-interactive scaffold (`--enterprise` for the full pack) |
|
|
54
|
+
| `agentx providers` | List LLM providers + required env vars |
|
|
55
|
+
| `agentx prompt list/set/add/remove` | Manage an existing project's prompts (`-d` opens the dashboard) |
|
|
56
|
+
| `agentx dashboard` | Prompt observability + optimization UI (`[dashboard]` extra) |
|
|
57
|
+
| `agentx cache stats / clear` | Inspect/clear the LLM response cache |
|
|
58
|
+
| `agentx mcp` | Run as an MCP server for Claude/Copilot/Codex |
|
|
59
|
+
| `agentx mcp --print-config` | Print the client config for those tools |
|
|
60
|
+
| `agentx version` | Show the installed version |
|
|
61
|
+
|
|
62
|
+
### ▶️ Try the demos (no API keys needed)
|
|
63
|
+
```bash
|
|
64
|
+
bash examples/demo_local.sh # verify local setup end-to-end
|
|
65
|
+
python examples/demo_mcp.py # test the Claude/Copilot MCP path (real handshake)
|
|
66
|
+
```
|
|
67
|
+
See [`examples/`](examples/) for details.
|
|
68
|
+
|
|
22
69
|
## 📦 Installation
|
|
23
70
|
|
|
24
71
|
### From PyPI (recommended)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# AgentX-Kit demos
|
|
2
|
+
|
|
3
|
+
Two runnable demos to confirm your setup — **no API keys required** (scaffolding
|
|
4
|
+
and insights are offline; LLM calls are optional).
|
|
5
|
+
|
|
6
|
+
## 1. Local setup test
|
|
7
|
+
Verifies the install, lists providers, scaffolds a demo project, and exercises
|
|
8
|
+
prompt insights + the response cache.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install "agentx-kit[all]"
|
|
12
|
+
bash examples/demo_local.sh
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 2. MCP connector test (the Claude / Copilot / Codex path)
|
|
16
|
+
Spawns `agentx mcp` over stdio, does a real MCP handshake, lists the tools, and
|
|
17
|
+
scaffolds a complete project from a one-line problem statement — exactly what an
|
|
18
|
+
assistant does when connected.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install "agentx-kit[connector]"
|
|
22
|
+
python examples/demo_mcp.py
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then wire it into Claude for real:
|
|
26
|
+
```bash
|
|
27
|
+
claude mcp add agentx-kit -- agentx mcp
|
|
28
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# AgentX-Kit — local setup smoke test. No API keys required.
|
|
3
|
+
# Verifies the install, lists providers, scaffolds a demo project, and exercises
|
|
4
|
+
# the prompt-insights + cache so you know everything works end-to-end.
|
|
5
|
+
#
|
|
6
|
+
# bash examples/demo_local.sh
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
# Resolve the CLI: prefer `agentx` on PATH, else `python -m agentx.cli`.
|
|
10
|
+
if command -v agentx >/dev/null 2>&1; then
|
|
11
|
+
AGENTX="agentx"
|
|
12
|
+
else
|
|
13
|
+
AGENTX="python -m agentx.cli"
|
|
14
|
+
fi
|
|
15
|
+
echo "▶ using: $AGENTX"
|
|
16
|
+
|
|
17
|
+
echo; echo "==> 1) version"
|
|
18
|
+
$AGENTX version
|
|
19
|
+
|
|
20
|
+
echo; echo "==> 2) providers (no keys needed to list)"
|
|
21
|
+
$AGENTX providers
|
|
22
|
+
|
|
23
|
+
WORK="$(mktemp -d)"; trap 'echo; echo "demo project left at: $WORK"' EXIT
|
|
24
|
+
echo; echo "==> 3) scaffold a demo project (local Ollama provider, no key)"
|
|
25
|
+
( cd "$WORK" && $AGENTX new --yes --name demo-agent --provider ollama \
|
|
26
|
+
--prompt "You are a helpful onboarding assistant." --no-venv )
|
|
27
|
+
echo "files:"
|
|
28
|
+
find "$WORK/demo-agent" -maxdepth 3 -type f | sed "s|$WORK/||" | sort
|
|
29
|
+
|
|
30
|
+
echo; echo "==> 4) prompt insights (offline)"
|
|
31
|
+
python - <<'PY'
|
|
32
|
+
from agentx import analyze_prompt, count_tokens
|
|
33
|
+
a = analyze_prompt("You are a support agent. Goal: answer in JSON. Do not invent policy.", "gpt-4o-mini")
|
|
34
|
+
print(f" quality={a.quality_score}/100 tokens={a.tokens} suggestions={len(a.suggestions)}")
|
|
35
|
+
PY
|
|
36
|
+
|
|
37
|
+
echo; echo "==> 5) response cache stats"
|
|
38
|
+
$AGENTX cache stats || true
|
|
39
|
+
|
|
40
|
+
echo; echo "✅ Local setup looks good."
|
|
41
|
+
echo "Next: cd $WORK/demo-agent && uv sync && uv run demo-agent"
|
|
42
|
+
echo " $AGENTX dashboard # prompt observability UI (needs [dashboard] extra)"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""AgentX-Kit — MCP connector demo (the same path Claude / Copilot / Codex use).
|
|
3
|
+
|
|
4
|
+
Spawns `agentx mcp` over stdio, performs a real MCP handshake, lists tools, and
|
|
5
|
+
calls them to scaffold a complete project from a one-line problem statement —
|
|
6
|
+
exactly what an MCP client (Claude) does. No API keys required (scaffolding is
|
|
7
|
+
template generation).
|
|
8
|
+
|
|
9
|
+
pip install "agentx-kit[connector]"
|
|
10
|
+
python examples/demo_mcp.py
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
import tempfile
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _extract(result) -> dict:
|
|
21
|
+
"""Pull a dict out of an MCP CallToolResult (structured or text content)."""
|
|
22
|
+
structured = getattr(result, "structuredContent", None)
|
|
23
|
+
if isinstance(structured, dict):
|
|
24
|
+
return structured.get("result", structured)
|
|
25
|
+
for block in getattr(result, "content", []) or []:
|
|
26
|
+
text = getattr(block, "text", None)
|
|
27
|
+
if text:
|
|
28
|
+
try:
|
|
29
|
+
return json.loads(text)
|
|
30
|
+
except json.JSONDecodeError:
|
|
31
|
+
return {"text": text}
|
|
32
|
+
return {}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def main() -> int:
|
|
36
|
+
try:
|
|
37
|
+
from mcp import ClientSession, StdioServerParameters
|
|
38
|
+
from mcp.client.stdio import stdio_client
|
|
39
|
+
except ImportError:
|
|
40
|
+
print("Install the connector extra first: pip install 'agentx-kit[connector]'")
|
|
41
|
+
return 1
|
|
42
|
+
|
|
43
|
+
# Launch the same server Claude would (use this interpreter for portability).
|
|
44
|
+
params = StdioServerParameters(command=sys.executable, args=["-m", "agentx.cli", "mcp"])
|
|
45
|
+
|
|
46
|
+
print("▶ connecting to `agentx mcp` over stdio …")
|
|
47
|
+
async with stdio_client(params) as (read, write):
|
|
48
|
+
async with ClientSession(read, write) as session:
|
|
49
|
+
await session.initialize()
|
|
50
|
+
|
|
51
|
+
tools = await session.list_tools()
|
|
52
|
+
print("✓ connected. tools:", [t.name for t in tools.tools])
|
|
53
|
+
|
|
54
|
+
problem = "Build a customer-support agent that answers from our product docs and serves a REST API."
|
|
55
|
+
print(f"\n▶ recommend_project('{problem[:50]}…')")
|
|
56
|
+
rec = _extract(await session.call_tool("recommend_project", {"problem_statement": problem}))
|
|
57
|
+
print(f" framework={rec.get('framework')} features={rec.get('features')}")
|
|
58
|
+
|
|
59
|
+
out_dir = tempfile.mkdtemp(prefix="agentx-mcp-demo-")
|
|
60
|
+
print("\n▶ create_agent_project(…)")
|
|
61
|
+
res = _extract(await session.call_tool(
|
|
62
|
+
"create_agent_project",
|
|
63
|
+
{"problem_statement": problem, "output_dir": out_dir + "/support-bot"},
|
|
64
|
+
))
|
|
65
|
+
if not res.get("ok"):
|
|
66
|
+
print(" ✗ failed:", res.get("error"))
|
|
67
|
+
return 1
|
|
68
|
+
print(f" ✓ generated {len(res['file_tree'])} files at {res['target_dir']}")
|
|
69
|
+
print(" features:", res["features"])
|
|
70
|
+
print(" run:", res["next_steps"][-1])
|
|
71
|
+
|
|
72
|
+
print("\n✅ MCP connector works — Claude/Copilot/Codex can drive this exact flow.")
|
|
73
|
+
print(" Add it to Claude: claude mcp add agentx-kit -- agentx mcp")
|
|
74
|
+
return 0
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
raise SystemExit(asyncio.run(main()))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
# PyPI distribution name (import name + CLI stay `agentx`; `agentx` was taken).
|
|
3
3
|
name = "agentx-kit"
|
|
4
|
-
version = "0.
|
|
4
|
+
version = "0.6.0"
|
|
5
5
|
description = "An open-source, provider-agnostic agentic framework + interactive project scaffolder for LangChain and CrewAI. Pick your LLM provider, agents, RAG, memory, MCP tools and skills — generate a ready-to-run uv project."
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
requires-python = ">=3.10,<3.14"
|
|
@@ -74,10 +74,11 @@ def build_project_from_statement(
|
|
|
74
74
|
# Collect a compact view for the calling LLM.
|
|
75
75
|
tree = sorted(str(p.relative_to(root)) for p in root.glob("**/*") if p.is_file())
|
|
76
76
|
pkg = spec.package
|
|
77
|
-
key_paths = [
|
|
78
|
-
|
|
79
|
-
f"src/{pkg}/
|
|
80
|
-
|
|
77
|
+
key_paths = ["pyproject.toml", "prompts.json", "agentx.json", f"src/{pkg}/main.py"]
|
|
78
|
+
if spec.framework == "langgraph":
|
|
79
|
+
key_paths += [f"src/{pkg}/graph.py", f"src/{pkg}/state.py", f"src/{pkg}/nodes.py"]
|
|
80
|
+
else:
|
|
81
|
+
key_paths += [f"src/{pkg}/crew.py", f"src/{pkg}/agents.py", f"src/{pkg}/tasks.py"]
|
|
81
82
|
if spec.serve:
|
|
82
83
|
key_paths.append(f"src/{pkg}/server.py")
|
|
83
84
|
key_files = {}
|
|
@@ -18,8 +18,8 @@ from .spec import ProjectSpec
|
|
|
18
18
|
|
|
19
19
|
TEMPLATES_DIR = Path(__file__).parent / "templates"
|
|
20
20
|
|
|
21
|
-
# (template, output-relative-path
|
|
22
|
-
|
|
21
|
+
# (template, output-relative-path) — output paths use {pkg} placeholder.
|
|
22
|
+
_COMMON_FILES: list[tuple[str, str]] = [
|
|
23
23
|
("pyproject.toml.j2", "pyproject.toml"),
|
|
24
24
|
("README.md.j2", "README.md"),
|
|
25
25
|
("env.example.j2", ".env.example"),
|
|
@@ -27,10 +27,24 @@ _FILE_PLAN: list[tuple[str, str]] = [
|
|
|
27
27
|
("pkg/__init__.py.j2", "src/{pkg}/__init__.py"),
|
|
28
28
|
("pkg/config.py.j2", "src/{pkg}/config.py"),
|
|
29
29
|
("pkg/prompts.py.j2", "src/{pkg}/prompts.py"),
|
|
30
|
-
("pkg/agents.py.j2", "src/{pkg}/agents.py"),
|
|
31
30
|
("pkg/main.py.j2", "src/{pkg}/main.py"),
|
|
32
31
|
]
|
|
33
32
|
|
|
33
|
+
# A real LangGraph project: explicit state + nodes + graph + tool assembly.
|
|
34
|
+
_LANGGRAPH_FILES: list[tuple[str, str]] = [
|
|
35
|
+
("pkg/state.py.j2", "src/{pkg}/state.py"),
|
|
36
|
+
("pkg/tools.py.j2", "src/{pkg}/tools.py"),
|
|
37
|
+
("pkg/nodes.py.j2", "src/{pkg}/nodes.py"),
|
|
38
|
+
("pkg/graph.py.j2", "src/{pkg}/graph.py"),
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
# A real CrewAI project: agents + tasks + crew.
|
|
42
|
+
_CREWAI_FILES: list[tuple[str, str]] = [
|
|
43
|
+
("pkg/agents.py.j2", "src/{pkg}/agents.py"),
|
|
44
|
+
("pkg/tasks.py.j2", "src/{pkg}/tasks.py"),
|
|
45
|
+
("pkg/crew.py.j2", "src/{pkg}/crew.py"),
|
|
46
|
+
]
|
|
47
|
+
|
|
34
48
|
|
|
35
49
|
@dataclass
|
|
36
50
|
class GenerationResult:
|
|
@@ -70,6 +84,7 @@ def _context(spec: ProjectSpec) -> dict:
|
|
|
70
84
|
"provider_env": list(provider_spec.env_vars),
|
|
71
85
|
"extras": _extras(spec),
|
|
72
86
|
"extras_str": ",".join(_extras(spec)),
|
|
87
|
+
"multi_agent": len(spec.agents) > 1,
|
|
73
88
|
}
|
|
74
89
|
|
|
75
90
|
|
|
@@ -85,12 +100,13 @@ def _env() -> Environment:
|
|
|
85
100
|
|
|
86
101
|
def _conditional_files(spec: ProjectSpec) -> list[tuple[str, str]]:
|
|
87
102
|
plan: list[tuple[str, str]] = []
|
|
103
|
+
# Framework-specific core (real project structure).
|
|
104
|
+
plan += _LANGGRAPH_FILES if spec.framework == "langgraph" else _CREWAI_FILES
|
|
88
105
|
if spec.use_rag:
|
|
89
106
|
plan.append(("pkg/rag.py.j2", "src/{pkg}/rag.py"))
|
|
90
107
|
if spec.needs_memory:
|
|
91
108
|
plan.append(("pkg/memory.py.j2", "src/{pkg}/memory.py"))
|
|
92
109
|
if spec.use_mcp:
|
|
93
|
-
plan.append(("pkg/tools.py.j2", "src/{pkg}/tools.py"))
|
|
94
110
|
plan.append(("mcp_servers.json.j2", "mcp_servers.json"))
|
|
95
111
|
if spec.use_skills:
|
|
96
112
|
plan.append(("skills_seed.json.j2", "data/skills/star-method.json"))
|
|
@@ -156,7 +172,7 @@ def generate_project(spec: ProjectSpec, target_dir: str | Path, overwrite: bool
|
|
|
156
172
|
written: list[Path] = []
|
|
157
173
|
messages: list[str] = []
|
|
158
174
|
|
|
159
|
-
for template_name, out_rel in
|
|
175
|
+
for template_name, out_rel in _COMMON_FILES + _conditional_files(spec):
|
|
160
176
|
out_path = target / out_rel.format(pkg=spec.package)
|
|
161
177
|
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
162
178
|
rendered = env.get_template(template_name).render(**ctx)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# {{ spec.slug }}
|
|
2
|
+
|
|
3
|
+
Agentic app generated by **AgentX**.
|
|
4
|
+
|
|
5
|
+
- **Framework:** {{ spec.framework }}
|
|
6
|
+
- **Provider:** {{ provider_label }} (`{{ model }}`)
|
|
7
|
+
- **Agents:** {% for a in spec.agents %}`{{ a.name }}`{% if not loop.last %}, {% endif %}{% endfor %}
|
|
8
|
+
|
|
9
|
+
- **RAG:** {{ "yes" if spec.use_rag else "no" }}
|
|
10
|
+
- **Memory:** {{ spec.memory }}
|
|
11
|
+
- **MCP tools:** {{ "yes" if spec.use_mcp else "no" }}
|
|
12
|
+
- **Skills:** {{ "yes" if spec.use_skills else "no" }}
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
```bash
|
|
16
|
+
uv sync # install dependencies into .venv
|
|
17
|
+
cp .env.example .env # then fill in your credentials
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Run
|
|
21
|
+
```bash
|
|
22
|
+
uv run {{ spec.slug }}
|
|
23
|
+
# or:
|
|
24
|
+
uv run python -m {{ pkg }}.main
|
|
25
|
+
{% if spec.serve %}# API server:
|
|
26
|
+
uv run uvicorn {{ pkg }}.server:app --reload
|
|
27
|
+
{% endif %}
|
|
28
|
+
```
|
|
29
|
+
{% if spec.framework == 'langgraph' %}
|
|
30
|
+
Uses LangGraph's dev server too: `uvx langgraph dev` (the compiled graph is
|
|
31
|
+
`{{ pkg }}.graph:graph`).
|
|
32
|
+
|
|
33
|
+
## Layout (LangGraph)
|
|
34
|
+
```
|
|
35
|
+
src/{{ pkg }}/
|
|
36
|
+
├── config.py # provider/model selection (pydantic-settings)
|
|
37
|
+
├── prompts.py # loads prompts.json (edit prompts there or via `agentx prompt`)
|
|
38
|
+
├── state.py # AgentState — the typed graph state (messages + reducers)
|
|
39
|
+
├── tools.py # tool assembly (web search{% if spec.use_rag %} + knowledge base{% endif %}{% if spec.use_mcp %} + MCP{% endif %})
|
|
40
|
+
├── nodes.py # graph nodes ({% if multi_agent %}supervisor + workers{% else %}the agent model call{% endif %})
|
|
41
|
+
├── graph.py # StateGraph workflow + compiled `graph` + run_text()
|
|
42
|
+
{% if spec.use_rag %}├── rag.py # knowledge base / retriever tool
|
|
43
|
+
{% endif %}
|
|
44
|
+
{% if spec.needs_memory %}├── memory.py # conversation / long-term memory helpers
|
|
45
|
+
{% endif %}
|
|
46
|
+
{% if spec.serve %}├── server.py # FastAPI (REST + SSE)
|
|
47
|
+
{% endif %}
|
|
48
|
+
└── main.py # interactive REPL
|
|
49
|
+
```
|
|
50
|
+
{% else %}
|
|
51
|
+
## Layout (CrewAI)
|
|
52
|
+
```
|
|
53
|
+
src/{{ pkg }}/
|
|
54
|
+
├── config.py # provider/model selection (pydantic-settings)
|
|
55
|
+
├── prompts.py # loads prompts.json
|
|
56
|
+
├── agents.py # CrewAI Agent definitions
|
|
57
|
+
├── tasks.py # Task definitions
|
|
58
|
+
├── crew.py # Crew assembly + run_text()
|
|
59
|
+
{% if spec.serve %}├── server.py # FastAPI (REST + SSE)
|
|
60
|
+
{% endif %}
|
|
61
|
+
└── main.py # interactive REPL
|
|
62
|
+
```
|
|
63
|
+
{% endif %}
|
|
64
|
+
{% if spec.use_rag %}
|
|
65
|
+
Add `.txt`/`.md` files under `knowledge/` to populate the RAG index.
|
|
66
|
+
{% endif %}
|
|
67
|
+
{% if spec.use_mcp %}
|
|
68
|
+
Edit `mcp_servers.json` to configure your MCP servers.
|
|
69
|
+
{% endif %}
|
|
@@ -17,10 +17,9 @@ from dotenv import load_dotenv
|
|
|
17
17
|
|
|
18
18
|
from agentx import get_chat_model
|
|
19
19
|
{% if spec.framework == 'langgraph' %}
|
|
20
|
-
from
|
|
21
|
-
from {{ pkg }}.agents import build_agents
|
|
20
|
+
from {{ pkg }}.graph import run_text
|
|
22
21
|
{% else %}
|
|
23
|
-
from {{ pkg }}.
|
|
22
|
+
from {{ pkg }}.crew import run_text
|
|
24
23
|
{% endif %}
|
|
25
24
|
|
|
26
25
|
THRESHOLD = 0.6
|
|
@@ -35,12 +34,7 @@ JUDGE_PROMPT = (
|
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
def _agent_reply(message: str) -> str:
|
|
38
|
-
|
|
39
|
-
agent = next(iter(build_agents().values()))
|
|
40
|
-
return run_agent(agent, message)
|
|
41
|
-
{% else %}
|
|
42
|
-
return str(build_project_crew(message).kickoff())
|
|
43
|
-
{% endif %}
|
|
37
|
+
return run_text(message)
|
|
44
38
|
|
|
45
39
|
|
|
46
40
|
def _score(judge, case: dict, output: str) -> float:
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Agent definitions (CrewAI) for {{ spec.slug }} — built from prompts.json."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from agentx.frameworks import build_crewai_agent
|
|
5
|
+
from .config import MODEL, PROVIDER
|
|
6
|
+
from .prompts import agents_meta, resolved_system_prompts
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def build_agents() -> dict:
|
|
10
|
+
"""Return {name: CrewAI Agent} — one per entry in prompts.json."""
|
|
11
|
+
prompts = resolved_system_prompts()
|
|
12
|
+
agents: dict = {}
|
|
13
|
+
for name, meta in agents_meta().items():
|
|
14
|
+
agents[name] = build_crewai_agent(
|
|
15
|
+
role=meta.get("role", name),
|
|
16
|
+
goal=meta.get("goal", "Help the user accomplish their task accurately."),
|
|
17
|
+
backstory=prompts.get(name, ""),
|
|
18
|
+
provider=PROVIDER,
|
|
19
|
+
model=MODEL,
|
|
20
|
+
)
|
|
21
|
+
return agents
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Crew assembly (CrewAI) for {{ spec.slug }}."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from agentx.frameworks import build_crew
|
|
5
|
+
from .agents import build_agents
|
|
6
|
+
from .tasks import build_tasks
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def build_project_crew(user_input: str):
|
|
10
|
+
"""Assemble a sequential crew of all agents for the user's request."""
|
|
11
|
+
agents = build_agents()
|
|
12
|
+
tasks = build_tasks(user_input, agents)
|
|
13
|
+
return build_crew(list(agents.values()), tasks)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run_text(message: str) -> str:
|
|
17
|
+
"""Run the crew on a message and return the final text (used by main/server)."""
|
|
18
|
+
return str(build_project_crew(message).kickoff())
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""LangGraph workflow for {{ spec.slug }}.
|
|
2
|
+
|
|
3
|
+
Exposes a module-level compiled ``graph`` (the LangGraph convention, also picked
|
|
4
|
+
up by ``langgraph dev``) and a ``run_text`` helper for CLI/server use.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from langgraph.checkpoint.memory import MemorySaver
|
|
9
|
+
from langgraph.graph import END, START, StateGraph
|
|
10
|
+
|
|
11
|
+
from .state import AgentState
|
|
12
|
+
{% if not multi_agent %}
|
|
13
|
+
from langgraph.prebuilt import ToolNode, tools_condition
|
|
14
|
+
|
|
15
|
+
from .nodes import call_model
|
|
16
|
+
from .tools import get_tools
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def build_graph():
|
|
20
|
+
"""Build the ReAct agent graph: agent ⇄ tools, with checkpointed memory."""
|
|
21
|
+
builder = StateGraph(AgentState)
|
|
22
|
+
builder.add_node("agent", call_model)
|
|
23
|
+
builder.add_node("tools", ToolNode(get_tools()))
|
|
24
|
+
|
|
25
|
+
builder.add_edge(START, "agent")
|
|
26
|
+
# If the model asked for a tool, go run it; otherwise finish.
|
|
27
|
+
builder.add_conditional_edges("agent", tools_condition)
|
|
28
|
+
builder.add_edge("tools", "agent")
|
|
29
|
+
|
|
30
|
+
return builder.compile(checkpointer=MemorySaver())
|
|
31
|
+
{% else %}
|
|
32
|
+
from .nodes import WORKERS, make_worker, supervisor
|
|
33
|
+
from .prompts import resolved_system_prompts
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def build_graph():
|
|
37
|
+
"""Build a supervisor multi-agent graph: supervisor routes to workers."""
|
|
38
|
+
builder = StateGraph(AgentState)
|
|
39
|
+
builder.add_node("supervisor", supervisor)
|
|
40
|
+
for name, system_prompt in resolved_system_prompts().items():
|
|
41
|
+
builder.add_node(name, make_worker(name, system_prompt))
|
|
42
|
+
|
|
43
|
+
builder.add_edge(START, "supervisor")
|
|
44
|
+
# Supervisor decides the next worker, or ends.
|
|
45
|
+
builder.add_conditional_edges(
|
|
46
|
+
"supervisor",
|
|
47
|
+
lambda state: state.get("next", "FINISH"),
|
|
48
|
+
{**{name: name for name in WORKERS}, "FINISH": END},
|
|
49
|
+
)
|
|
50
|
+
# Every worker reports back to the supervisor.
|
|
51
|
+
for name in WORKERS:
|
|
52
|
+
builder.add_edge(name, "supervisor")
|
|
53
|
+
|
|
54
|
+
return builder.compile(checkpointer=MemorySaver())
|
|
55
|
+
{% endif %}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Module-level compiled graph (import this, or run `langgraph dev`).
|
|
59
|
+
graph = build_graph()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def run_text(message: str, thread_id: str = "default") -> str:
|
|
63
|
+
"""Invoke the graph with one user message; return the final text reply."""
|
|
64
|
+
result = graph.invoke(
|
|
65
|
+
{"messages": [{"role": "user", "content": message}]},
|
|
66
|
+
config={"configurable": {"thread_id": thread_id}},
|
|
67
|
+
)
|
|
68
|
+
messages = result.get("messages", [])
|
|
69
|
+
return getattr(messages[-1], "content", "") if messages else ""
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Entry point for {{ spec.slug }} — an interactive REPL over the agent graph."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dotenv import load_dotenv
|
|
5
|
+
{% if spec.observability %}from .observability import init_observability
|
|
6
|
+
{% endif %}
|
|
7
|
+
{% if spec.guardrails %}from .guardrails import guard_input, guard_output
|
|
8
|
+
{% endif %}
|
|
9
|
+
{% if spec.use_cache %}from agentx.cache import enable_caching
|
|
10
|
+
{% endif %}
|
|
11
|
+
{% if spec.framework == 'langgraph' %}from .graph import run_text
|
|
12
|
+
{% else %}from .crew import run_text
|
|
13
|
+
{% endif %}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main() -> None:
|
|
17
|
+
load_dotenv()
|
|
18
|
+
{% if spec.observability %}
|
|
19
|
+
init_observability()
|
|
20
|
+
{% endif %}
|
|
21
|
+
{% if spec.use_cache %}
|
|
22
|
+
enable_caching() # cache LLM responses → lower cost & latency
|
|
23
|
+
{% endif %}
|
|
24
|
+
print("🧬 {{ spec.slug }} ready. Type 'quit' to exit.\n")
|
|
25
|
+
while True:
|
|
26
|
+
try:
|
|
27
|
+
user = input("you > ").strip()
|
|
28
|
+
except (EOFError, KeyboardInterrupt):
|
|
29
|
+
break
|
|
30
|
+
if user.lower() in {"quit", "exit"}:
|
|
31
|
+
break
|
|
32
|
+
if not user:
|
|
33
|
+
continue
|
|
34
|
+
{% if spec.guardrails %}
|
|
35
|
+
user = guard_input(user).text
|
|
36
|
+
{% endif %}
|
|
37
|
+
reply = run_text(user)
|
|
38
|
+
{% if spec.guardrails %}
|
|
39
|
+
reply = guard_output(reply).text
|
|
40
|
+
{% endif %}
|
|
41
|
+
print(f"\nbot > {reply}\n")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
main()
|