flowai-cli 0.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.
- flowai_cli-0.2.0/.gitignore +11 -0
- flowai_cli-0.2.0/LICENSE +21 -0
- flowai_cli-0.2.0/PKG-INFO +223 -0
- flowai_cli-0.2.0/README.md +171 -0
- flowai_cli-0.2.0/examples/01_quickstart.py +36 -0
- flowai_cli-0.2.0/examples/02_nvidia_deepseek.py +40 -0
- flowai_cli-0.2.0/examples/03_workflow_graph.py +56 -0
- flowai_cli-0.2.0/examples/04_fastmcp_agent.py +37 -0
- flowai_cli-0.2.0/examples/05_debate_and_swarm.py +68 -0
- flowai_cli-0.2.0/flowai/__init__.py +72 -0
- flowai_cli-0.2.0/flowai/__main__.py +8 -0
- flowai_cli-0.2.0/flowai/agents/__init__.py +21 -0
- flowai_cli-0.2.0/flowai/agents/base.py +61 -0
- flowai_cli-0.2.0/flowai/agents/patterns/__init__.py +11 -0
- flowai_cli-0.2.0/flowai/agents/patterns/debate.py +79 -0
- flowai_cli-0.2.0/flowai/agents/patterns/reflection.py +74 -0
- flowai_cli-0.2.0/flowai/agents/specialized.py +87 -0
- flowai_cli-0.2.0/flowai/agents/standard.py +176 -0
- flowai_cli-0.2.0/flowai/cli/__init__.py +5 -0
- flowai_cli-0.2.0/flowai/cli/main.py +562 -0
- flowai_cli-0.2.0/flowai/cli/ui.py +98 -0
- flowai_cli-0.2.0/flowai/config/__init__.py +5 -0
- flowai_cli-0.2.0/flowai/config/settings.py +204 -0
- flowai_cli-0.2.0/flowai/errors.py +30 -0
- flowai_cli-0.2.0/flowai/mcp/__init__.py +5 -0
- flowai_cli-0.2.0/flowai/mcp/client.py +77 -0
- flowai_cli-0.2.0/flowai/memory/__init__.py +16 -0
- flowai_cli-0.2.0/flowai/memory/base.py +45 -0
- flowai_cli-0.2.0/flowai/memory/chroma.py +97 -0
- flowai_cli-0.2.0/flowai/memory/in_memory.py +65 -0
- flowai_cli-0.2.0/flowai/memory/qdrant.py +104 -0
- flowai_cli-0.2.0/flowai/observability/__init__.py +15 -0
- flowai_cli-0.2.0/flowai/observability/events.py +45 -0
- flowai_cli-0.2.0/flowai/observability/opentelemetry.py +56 -0
- flowai_cli-0.2.0/flowai/observability/tracer.py +80 -0
- flowai_cli-0.2.0/flowai/plugins/__init__.py +5 -0
- flowai_cli-0.2.0/flowai/plugins/loader.py +31 -0
- flowai_cli-0.2.0/flowai/providers/__init__.py +35 -0
- flowai_cli-0.2.0/flowai/providers/anthropic.py +268 -0
- flowai_cli-0.2.0/flowai/providers/base.py +180 -0
- flowai_cli-0.2.0/flowai/providers/gemini.py +230 -0
- flowai_cli-0.2.0/flowai/providers/nvidia.py +57 -0
- flowai_cli-0.2.0/flowai/providers/ollama.py +237 -0
- flowai_cli-0.2.0/flowai/providers/openai.py +274 -0
- flowai_cli-0.2.0/flowai/providers/registry.py +50 -0
- flowai_cli-0.2.0/flowai/runtime/__init__.py +5 -0
- flowai_cli-0.2.0/flowai/runtime/session.py +76 -0
- flowai_cli-0.2.0/flowai/server/__init__.py +5 -0
- flowai_cli-0.2.0/flowai/server/app.py +133 -0
- flowai_cli-0.2.0/flowai/tools/__init__.py +39 -0
- flowai_cli-0.2.0/flowai/tools/base.py +56 -0
- flowai_cli-0.2.0/flowai/tools/builtin.py +128 -0
- flowai_cli-0.2.0/flowai/tools/decorator.py +103 -0
- flowai_cli-0.2.0/flowai/tools/files.py +197 -0
- flowai_cli-0.2.0/flowai/tools/registry.py +84 -0
- flowai_cli-0.2.0/flowai/tools/web.py +94 -0
- flowai_cli-0.2.0/flowai/workflow/__init__.py +15 -0
- flowai_cli-0.2.0/flowai/workflow/checkpoint.py +73 -0
- flowai_cli-0.2.0/flowai/workflow/engine.py +144 -0
- flowai_cli-0.2.0/flowai/workflow/state.py +43 -0
- flowai_cli-0.2.0/flowai.yaml +36 -0
- flowai_cli-0.2.0/pyproject.toml +87 -0
- flowai_cli-0.2.0/tests/test_agents.py +90 -0
- flowai_cli-0.2.0/tests/test_builtin_tools.py +124 -0
- flowai_cli-0.2.0/tests/test_cli.py +126 -0
- flowai_cli-0.2.0/tests/test_memory.py +42 -0
- flowai_cli-0.2.0/tests/test_observability.py +38 -0
- flowai_cli-0.2.0/tests/test_server.py +38 -0
- flowai_cli-0.2.0/tests/test_session.py +29 -0
- flowai_cli-0.2.0/tests/test_streaming.py +174 -0
- flowai_cli-0.2.0/tests/test_tools.py +55 -0
- flowai_cli-0.2.0/tests/test_workflow.py +61 -0
flowai_cli-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ajay Rama Swamy Katragaada
|
|
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.
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: flowai-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: FlowAI: an open-source, extensible Python agent harness with a terminal-first CLI.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Ajayramaswamy2005/FlowAI
|
|
6
|
+
Project-URL: Source, https://github.com/Ajayramaswamy2005/FlowAI
|
|
7
|
+
Project-URL: Issues, https://github.com/Ajayramaswamy2005/FlowAI/issues
|
|
8
|
+
Author: Ajay Rama Swamy Katragaada
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent,cli,fastapi,llm,mcp,nvidia,ollama,pydantic,workflow
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: httpx>=0.27.0
|
|
23
|
+
Requires-Dist: pydantic>=2.7.0
|
|
24
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
25
|
+
Provides-Extra: all
|
|
26
|
+
Requires-Dist: chromadb>=0.5.0; extra == 'all'
|
|
27
|
+
Requires-Dist: fastapi>=0.111.0; extra == 'all'
|
|
28
|
+
Requires-Dist: mcp>=1.0.0; extra == 'all'
|
|
29
|
+
Requires-Dist: opentelemetry-api>=1.25.0; extra == 'all'
|
|
30
|
+
Requires-Dist: opentelemetry-sdk>=1.25.0; extra == 'all'
|
|
31
|
+
Requires-Dist: qdrant-client>=1.9.0; extra == 'all'
|
|
32
|
+
Requires-Dist: uvicorn[standard]>=0.30.0; extra == 'all'
|
|
33
|
+
Provides-Extra: chroma
|
|
34
|
+
Requires-Dist: chromadb>=0.5.0; extra == 'chroma'
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: fastapi>=0.111.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: httpx>=0.27.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
41
|
+
Provides-Extra: mcp
|
|
42
|
+
Requires-Dist: mcp>=1.0.0; extra == 'mcp'
|
|
43
|
+
Provides-Extra: observability
|
|
44
|
+
Requires-Dist: opentelemetry-api>=1.25.0; extra == 'observability'
|
|
45
|
+
Requires-Dist: opentelemetry-sdk>=1.25.0; extra == 'observability'
|
|
46
|
+
Provides-Extra: qdrant
|
|
47
|
+
Requires-Dist: qdrant-client>=1.9.0; extra == 'qdrant'
|
|
48
|
+
Provides-Extra: server
|
|
49
|
+
Requires-Dist: fastapi>=0.111.0; extra == 'server'
|
|
50
|
+
Requires-Dist: uvicorn[standard]>=0.30.0; extra == 'server'
|
|
51
|
+
Description-Content-Type: text/markdown
|
|
52
|
+
|
|
53
|
+
# FlowAI
|
|
54
|
+
|
|
55
|
+
FlowAI is an AI agent you run in your terminal, in the same spirit as Claude
|
|
56
|
+
Code or OpenCode. Bring your own key from Anthropic, OpenAI, NVIDIA, Google
|
|
57
|
+
Gemini or OpenRouter, or run fully local with Ollama. The agent can read, search
|
|
58
|
+
and edit the files in your project, run shell commands (after asking you), and
|
|
59
|
+
search the web.
|
|
60
|
+
|
|
61
|
+
It is also a Python library: the same agents, tools, workflows and memory the
|
|
62
|
+
CLI uses are yours to build on.
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install flowai-cli # once it is published to PyPI
|
|
68
|
+
pipx install flowai-cli # same, in its own isolated environment
|
|
69
|
+
|
|
70
|
+
# from a checkout of this repository:
|
|
71
|
+
pip install .
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Python 3.10 or newer. The package is `flowai-cli` (the name `flowai` is taken on PyPI);
|
|
75
|
+
the command it installs is `flowai`, and the import is `import flowai`.
|
|
76
|
+
|
|
77
|
+
## First run
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
flowai
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The first time, FlowAI asks for a provider and an API key and saves them to
|
|
84
|
+
`~/.flowai/config.json`. After that, `flowai` opens a session in the current
|
|
85
|
+
directory:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
------------------------------------------------------------
|
|
89
|
+
FlowAI 0.2.0 anthropic / claude-sonnet-5-5
|
|
90
|
+
workspace: /home/you/my-project
|
|
91
|
+
session: ses_3f9a1c2e
|
|
92
|
+
/help for commands, /exit to quit
|
|
93
|
+
------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
> why does the test in tests/test_api.py fail?
|
|
96
|
+
|
|
97
|
+
[tool] grep_files(pattern=def test_, path=tests)
|
|
98
|
+
[done] tests/test_api.py:12: def test_create_user(): (+4 more lines)
|
|
99
|
+
[tool] read_file(path=tests/test_api.py)
|
|
100
|
+
...
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Set up non-interactively instead (for scripts or CI):
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
flowai configure --provider anthropic --key "$ANTHROPIC_API_KEY"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Or skip configuration entirely: if `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`,
|
|
110
|
+
`NVIDIA_API_KEY`, `GEMINI_API_KEY` or `OPENROUTER_API_KEY` is set, FlowAI uses
|
|
111
|
+
it.
|
|
112
|
+
|
|
113
|
+
## Commands
|
|
114
|
+
|
|
115
|
+
| Command | What it does |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `flowai` | Interactive session in the current directory |
|
|
118
|
+
| `flowai "add type hints to utils.py"` | Run one task and exit |
|
|
119
|
+
| `flowai chat --resume` (or `-c`) | Continue the most recent session |
|
|
120
|
+
| `flowai run -q "..."` | One task, print only the final answer (good for scripts) |
|
|
121
|
+
| `echo "..." \| flowai run` | Read the prompt from stdin |
|
|
122
|
+
| `flowai configure` | Choose provider, model and API key |
|
|
123
|
+
| `flowai init [dir]` | Create a `flowai.yaml` and a starter `main.py` |
|
|
124
|
+
| `flowai inspect` | Show config status, saved sessions, checkpoints |
|
|
125
|
+
| `flowai tools` | List the built-in tools |
|
|
126
|
+
| `flowai serve` | HTTP API with streaming (`pip install 'flowai-cli[server]'`) |
|
|
127
|
+
|
|
128
|
+
Options for `chat` and `run`: `--provider`, `--model`, `-w/--workspace DIR`,
|
|
129
|
+
`--yolo` (do not ask before shell/python tools run), `--no-tools`,
|
|
130
|
+
`--max-iterations N`.
|
|
131
|
+
|
|
132
|
+
Inside a session: `/help`, `/model <name>`, `/provider <name> [model]`,
|
|
133
|
+
`/tools`, `/cost`, `/new`, `/reset`, `/yolo`, `/exit`.
|
|
134
|
+
|
|
135
|
+
## What the agent can do
|
|
136
|
+
|
|
137
|
+
| Tool | Notes |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `read_file`, `list_dir`, `glob_files`, `grep_files` | Read-only; never asks |
|
|
140
|
+
| `write_file`, `edit_file` | Changes files inside the workspace only |
|
|
141
|
+
| `bash` | Asks first. Runs in the workspace, 120 s timeout |
|
|
142
|
+
| `python_repl` | Asks first. Runs code in a fresh namespace |
|
|
143
|
+
| `search_web`, `fetch_url` | DuckDuckGo search and page fetch |
|
|
144
|
+
|
|
145
|
+
**Safety.** Every file path is resolved against the workspace (the directory
|
|
146
|
+
you started in, or `-w`) and refused if it points outside. Shell, Python and
|
|
147
|
+
MCP tools show you exactly what they will run and wait for `y`, `n` or `a`
|
|
148
|
+
(always, for that tool, this session). With no terminal attached, those tools
|
|
149
|
+
are refused rather than run. `--yolo` or `approval: "auto"` in `flowai.yaml`
|
|
150
|
+
turns the prompt off; use it only in a sandbox.
|
|
151
|
+
|
|
152
|
+
## Configuration
|
|
153
|
+
|
|
154
|
+
Settings are layered, later wins:
|
|
155
|
+
|
|
156
|
+
1. `~/.flowai/config.json`, written by `flowai configure`
|
|
157
|
+
2. `flowai.yaml` (or `.yml`, `.toml`, `.json`) in the current directory or any
|
|
158
|
+
parent, so it works from a subdirectory of your project
|
|
159
|
+
3. Environment: `FLOWAI_PROVIDER`, `FLOWAI_MODEL`, and the provider key variables
|
|
160
|
+
|
|
161
|
+
See [flowai.yaml](flowai.yaml) for every option, including `mcp_servers` for
|
|
162
|
+
borrowing tools from Model Context Protocol servers (`pip install 'flowai-cli[mcp]'`).
|
|
163
|
+
|
|
164
|
+
## Using it as a library
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
import asyncio
|
|
168
|
+
from flowai import Agent, tool
|
|
169
|
+
|
|
170
|
+
@tool
|
|
171
|
+
def compound_interest(principal: float, rate: float, years: int) -> float:
|
|
172
|
+
"""Balance after compounding yearly."""
|
|
173
|
+
return round(principal * (1 + rate) ** years, 2)
|
|
174
|
+
|
|
175
|
+
agent = Agent(
|
|
176
|
+
name="Advisor",
|
|
177
|
+
provider="anthropic", # nvidia, ollama, openai, gemini, openrouter
|
|
178
|
+
model="claude-sonnet-5-5",
|
|
179
|
+
tools=[compound_interest],
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
print(asyncio.run(agent.run("What will $5,000 be after 10 years at 8%?")))
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`agent.stream(prompt)` yields tokens as they arrive and runs tool calls along
|
|
186
|
+
the way. Tools you register in your own code run without an approval prompt
|
|
187
|
+
(you wrote them); give a `ToolRegistry` an `approver` callback to change that.
|
|
188
|
+
|
|
189
|
+
Beyond a single agent the library has:
|
|
190
|
+
|
|
191
|
+
- **Specialised agents**: `PlannerAgent`, `CriticAgent`, `ToolAgent`, `SupervisorAgent`
|
|
192
|
+
- **Patterns**: `ReflectionPattern`, `PlanAndExecutePattern`, `DebatePattern`, `Swarm`
|
|
193
|
+
- **Workflows**: a graph engine with conditional edges, loops, parallel
|
|
194
|
+
branches and checkpointing (`Workflow`, `FileCheckpointer`)
|
|
195
|
+
- **Memory**: `InMemory`, `QdrantMemory` (`flowai-cli[qdrant]`), `ChromaMemory` (`flowai-cli[chroma]`)
|
|
196
|
+
- **Observability**: per-span token counts and estimated cost (`agent.tracer`),
|
|
197
|
+
an event bus, and an OpenTelemetry exporter (`flowai-cli[observability]`)
|
|
198
|
+
- **Plugins**: third-party providers via the `flowai.plugins` entry point
|
|
199
|
+
|
|
200
|
+
Runnable examples are in [examples/](examples/).
|
|
201
|
+
|
|
202
|
+
## Developing FlowAI
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
pip install -e '.[dev]'
|
|
206
|
+
pytest
|
|
207
|
+
ruff check flowai tests
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The tests are offline: providers are faked at the HTTP layer, so no key is needed.
|
|
211
|
+
|
|
212
|
+
## Releasing to PyPI
|
|
213
|
+
|
|
214
|
+
1. Bump `version` in `pyproject.toml` and `__version__` in `flowai/__init__.py`.
|
|
215
|
+
2. Push a tag `flowai-vX.Y.Z`. The `flowai-release` workflow builds this
|
|
216
|
+
directory and publishes it with PyPI Trusted Publishing; configure the
|
|
217
|
+
`flowai-cli` project on PyPI to trust this repository and workflow first.
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
## License
|
|
222
|
+
|
|
223
|
+
MIT
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# FlowAI
|
|
2
|
+
|
|
3
|
+
FlowAI is an AI agent you run in your terminal, in the same spirit as Claude
|
|
4
|
+
Code or OpenCode. Bring your own key from Anthropic, OpenAI, NVIDIA, Google
|
|
5
|
+
Gemini or OpenRouter, or run fully local with Ollama. The agent can read, search
|
|
6
|
+
and edit the files in your project, run shell commands (after asking you), and
|
|
7
|
+
search the web.
|
|
8
|
+
|
|
9
|
+
It is also a Python library: the same agents, tools, workflows and memory the
|
|
10
|
+
CLI uses are yours to build on.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install flowai-cli # once it is published to PyPI
|
|
16
|
+
pipx install flowai-cli # same, in its own isolated environment
|
|
17
|
+
|
|
18
|
+
# from a checkout of this repository:
|
|
19
|
+
pip install .
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Python 3.10 or newer. The package is `flowai-cli` (the name `flowai` is taken on PyPI);
|
|
23
|
+
the command it installs is `flowai`, and the import is `import flowai`.
|
|
24
|
+
|
|
25
|
+
## First run
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
flowai
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The first time, FlowAI asks for a provider and an API key and saves them to
|
|
32
|
+
`~/.flowai/config.json`. After that, `flowai` opens a session in the current
|
|
33
|
+
directory:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
------------------------------------------------------------
|
|
37
|
+
FlowAI 0.2.0 anthropic / claude-sonnet-5-5
|
|
38
|
+
workspace: /home/you/my-project
|
|
39
|
+
session: ses_3f9a1c2e
|
|
40
|
+
/help for commands, /exit to quit
|
|
41
|
+
------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
> why does the test in tests/test_api.py fail?
|
|
44
|
+
|
|
45
|
+
[tool] grep_files(pattern=def test_, path=tests)
|
|
46
|
+
[done] tests/test_api.py:12: def test_create_user(): (+4 more lines)
|
|
47
|
+
[tool] read_file(path=tests/test_api.py)
|
|
48
|
+
...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Set up non-interactively instead (for scripts or CI):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
flowai configure --provider anthropic --key "$ANTHROPIC_API_KEY"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or skip configuration entirely: if `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`,
|
|
58
|
+
`NVIDIA_API_KEY`, `GEMINI_API_KEY` or `OPENROUTER_API_KEY` is set, FlowAI uses
|
|
59
|
+
it.
|
|
60
|
+
|
|
61
|
+
## Commands
|
|
62
|
+
|
|
63
|
+
| Command | What it does |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `flowai` | Interactive session in the current directory |
|
|
66
|
+
| `flowai "add type hints to utils.py"` | Run one task and exit |
|
|
67
|
+
| `flowai chat --resume` (or `-c`) | Continue the most recent session |
|
|
68
|
+
| `flowai run -q "..."` | One task, print only the final answer (good for scripts) |
|
|
69
|
+
| `echo "..." \| flowai run` | Read the prompt from stdin |
|
|
70
|
+
| `flowai configure` | Choose provider, model and API key |
|
|
71
|
+
| `flowai init [dir]` | Create a `flowai.yaml` and a starter `main.py` |
|
|
72
|
+
| `flowai inspect` | Show config status, saved sessions, checkpoints |
|
|
73
|
+
| `flowai tools` | List the built-in tools |
|
|
74
|
+
| `flowai serve` | HTTP API with streaming (`pip install 'flowai-cli[server]'`) |
|
|
75
|
+
|
|
76
|
+
Options for `chat` and `run`: `--provider`, `--model`, `-w/--workspace DIR`,
|
|
77
|
+
`--yolo` (do not ask before shell/python tools run), `--no-tools`,
|
|
78
|
+
`--max-iterations N`.
|
|
79
|
+
|
|
80
|
+
Inside a session: `/help`, `/model <name>`, `/provider <name> [model]`,
|
|
81
|
+
`/tools`, `/cost`, `/new`, `/reset`, `/yolo`, `/exit`.
|
|
82
|
+
|
|
83
|
+
## What the agent can do
|
|
84
|
+
|
|
85
|
+
| Tool | Notes |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `read_file`, `list_dir`, `glob_files`, `grep_files` | Read-only; never asks |
|
|
88
|
+
| `write_file`, `edit_file` | Changes files inside the workspace only |
|
|
89
|
+
| `bash` | Asks first. Runs in the workspace, 120 s timeout |
|
|
90
|
+
| `python_repl` | Asks first. Runs code in a fresh namespace |
|
|
91
|
+
| `search_web`, `fetch_url` | DuckDuckGo search and page fetch |
|
|
92
|
+
|
|
93
|
+
**Safety.** Every file path is resolved against the workspace (the directory
|
|
94
|
+
you started in, or `-w`) and refused if it points outside. Shell, Python and
|
|
95
|
+
MCP tools show you exactly what they will run and wait for `y`, `n` or `a`
|
|
96
|
+
(always, for that tool, this session). With no terminal attached, those tools
|
|
97
|
+
are refused rather than run. `--yolo` or `approval: "auto"` in `flowai.yaml`
|
|
98
|
+
turns the prompt off; use it only in a sandbox.
|
|
99
|
+
|
|
100
|
+
## Configuration
|
|
101
|
+
|
|
102
|
+
Settings are layered, later wins:
|
|
103
|
+
|
|
104
|
+
1. `~/.flowai/config.json`, written by `flowai configure`
|
|
105
|
+
2. `flowai.yaml` (or `.yml`, `.toml`, `.json`) in the current directory or any
|
|
106
|
+
parent, so it works from a subdirectory of your project
|
|
107
|
+
3. Environment: `FLOWAI_PROVIDER`, `FLOWAI_MODEL`, and the provider key variables
|
|
108
|
+
|
|
109
|
+
See [flowai.yaml](flowai.yaml) for every option, including `mcp_servers` for
|
|
110
|
+
borrowing tools from Model Context Protocol servers (`pip install 'flowai-cli[mcp]'`).
|
|
111
|
+
|
|
112
|
+
## Using it as a library
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
import asyncio
|
|
116
|
+
from flowai import Agent, tool
|
|
117
|
+
|
|
118
|
+
@tool
|
|
119
|
+
def compound_interest(principal: float, rate: float, years: int) -> float:
|
|
120
|
+
"""Balance after compounding yearly."""
|
|
121
|
+
return round(principal * (1 + rate) ** years, 2)
|
|
122
|
+
|
|
123
|
+
agent = Agent(
|
|
124
|
+
name="Advisor",
|
|
125
|
+
provider="anthropic", # nvidia, ollama, openai, gemini, openrouter
|
|
126
|
+
model="claude-sonnet-5-5",
|
|
127
|
+
tools=[compound_interest],
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
print(asyncio.run(agent.run("What will $5,000 be after 10 years at 8%?")))
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`agent.stream(prompt)` yields tokens as they arrive and runs tool calls along
|
|
134
|
+
the way. Tools you register in your own code run without an approval prompt
|
|
135
|
+
(you wrote them); give a `ToolRegistry` an `approver` callback to change that.
|
|
136
|
+
|
|
137
|
+
Beyond a single agent the library has:
|
|
138
|
+
|
|
139
|
+
- **Specialised agents**: `PlannerAgent`, `CriticAgent`, `ToolAgent`, `SupervisorAgent`
|
|
140
|
+
- **Patterns**: `ReflectionPattern`, `PlanAndExecutePattern`, `DebatePattern`, `Swarm`
|
|
141
|
+
- **Workflows**: a graph engine with conditional edges, loops, parallel
|
|
142
|
+
branches and checkpointing (`Workflow`, `FileCheckpointer`)
|
|
143
|
+
- **Memory**: `InMemory`, `QdrantMemory` (`flowai-cli[qdrant]`), `ChromaMemory` (`flowai-cli[chroma]`)
|
|
144
|
+
- **Observability**: per-span token counts and estimated cost (`agent.tracer`),
|
|
145
|
+
an event bus, and an OpenTelemetry exporter (`flowai-cli[observability]`)
|
|
146
|
+
- **Plugins**: third-party providers via the `flowai.plugins` entry point
|
|
147
|
+
|
|
148
|
+
Runnable examples are in [examples/](examples/).
|
|
149
|
+
|
|
150
|
+
## Developing FlowAI
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
pip install -e '.[dev]'
|
|
154
|
+
pytest
|
|
155
|
+
ruff check flowai tests
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The tests are offline: providers are faked at the HTTP layer, so no key is needed.
|
|
159
|
+
|
|
160
|
+
## Releasing to PyPI
|
|
161
|
+
|
|
162
|
+
1. Bump `version` in `pyproject.toml` and `__version__` in `flowai/__init__.py`.
|
|
163
|
+
2. Push a tag `flowai-vX.Y.Z`. The `flowai-release` workflow builds this
|
|
164
|
+
directory and publishes it with PyPI Trusted Publishing; configure the
|
|
165
|
+
`flowai-cli` project on PyPI to trust this repository and workflow first.
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Example 01: Quickstart with FlowAI."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from flowai import Agent, tool
|
|
6
|
+
from flowai.memory import ConversationMemory
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@tool
|
|
10
|
+
def calculate_growth(initial: float, rate: float, periods: int) -> float:
|
|
11
|
+
"""Calculate compound interest growth."""
|
|
12
|
+
return round(initial * ((1 + rate) ** periods), 2)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def main():
|
|
16
|
+
# Initialize an agent with in-memory conversation memory
|
|
17
|
+
memory = ConversationMemory()
|
|
18
|
+
agent = Agent(
|
|
19
|
+
name="FinanceAgent",
|
|
20
|
+
provider="openai", # or "nvidia", "ollama", "anthropic", "gemini"
|
|
21
|
+
model="gpt-4o-mini",
|
|
22
|
+
tools=[calculate_growth],
|
|
23
|
+
memory=memory,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
print("=== FlowAI Quickstart Agent ===")
|
|
27
|
+
response = await agent.run("If I invest $10,000 at 7% for 5 years, what will it be worth?")
|
|
28
|
+
print(f"Agent Reply:\n{response}\n")
|
|
29
|
+
|
|
30
|
+
# Conversation history is preserved
|
|
31
|
+
print("=== Preserved Conversation History ===")
|
|
32
|
+
print(memory.format_history())
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Example 02: NVIDIA API Catalog with DeepSeek-R1 / DeepSeek-V4."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from flowai import Agent, tool
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@tool
|
|
9
|
+
def check_server_health(host: str) -> dict:
|
|
10
|
+
"""Check system resource status and latency."""
|
|
11
|
+
return {"host": host, "status": "healthy", "cpu_percent": 24.5, "latency_ms": 12.4}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def main():
|
|
15
|
+
# Make sure NVIDIA_API_KEY is set in your environment
|
|
16
|
+
agent = Agent(
|
|
17
|
+
name="NvidiaDeepSeekAgent",
|
|
18
|
+
provider="nvidia",
|
|
19
|
+
model="deepseek-ai/deepseek-r1",
|
|
20
|
+
system_prompt="You are a DevOps autonomous triage engineer.",
|
|
21
|
+
tools=[check_server_health],
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
print("=== Invoking DeepSeek on NVIDIA API Catalog ===")
|
|
25
|
+
prompt = "Check the health of 'prod-cluster-us-east-1' and summarize your findings."
|
|
26
|
+
|
|
27
|
+
# Non-streaming invocation
|
|
28
|
+
result = await agent.run(prompt)
|
|
29
|
+
print("\n--- Agent Conclusion ---")
|
|
30
|
+
print(result)
|
|
31
|
+
|
|
32
|
+
# Token streaming invocation
|
|
33
|
+
print("\n--- Streaming Response ---")
|
|
34
|
+
async for chunk in agent.stream("Recommend 2 optimization actions."):
|
|
35
|
+
print(chunk.text_delta, end="", flush=True)
|
|
36
|
+
print()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Example 03: Multi-agent Workflow DAG with State Passing and Checkpoints."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from flowai import Agent, Workflow
|
|
6
|
+
from flowai.workflow.checkpoint import InMemoryCheckpointer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def main():
|
|
10
|
+
# 1. Instantiate specialized agents
|
|
11
|
+
researcher = Agent(
|
|
12
|
+
name="Researcher",
|
|
13
|
+
model="gpt-4o-mini",
|
|
14
|
+
system_prompt="You are a Technical Researcher. Provide facts, architecture patterns, and benchmarks.",
|
|
15
|
+
)
|
|
16
|
+
coder = Agent(
|
|
17
|
+
name="Coder",
|
|
18
|
+
model="gpt-4o-mini",
|
|
19
|
+
system_prompt="You are a Principal Software Engineer. Write clean, idiomatic Python code based on research.",
|
|
20
|
+
)
|
|
21
|
+
reviewer = Agent(
|
|
22
|
+
name="Reviewer",
|
|
23
|
+
model="gpt-4o-mini",
|
|
24
|
+
system_prompt="You are a Code Reviewer. Inspect code for bugs, edge cases, and performance bottlenecks.",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# 2. Construct DAG Workflow with Checkpointing
|
|
28
|
+
checkpointer = InMemoryCheckpointer()
|
|
29
|
+
wf = Workflow(name="CodeGenerationPipeline", checkpointer=checkpointer)
|
|
30
|
+
|
|
31
|
+
wf.add_agent(researcher, name="research_node")
|
|
32
|
+
wf.add_agent(coder, name="coding_node")
|
|
33
|
+
wf.add_agent(reviewer, name="review_node")
|
|
34
|
+
|
|
35
|
+
# Connect nodes in a linear sequential pipeline
|
|
36
|
+
wf.add_edge("research_node", "coding_node")
|
|
37
|
+
wf.add_edge("coding_node", "review_node")
|
|
38
|
+
|
|
39
|
+
# 3. Execute workflow
|
|
40
|
+
task = "Design and implement a thread-safe token bucket rate limiter in Python."
|
|
41
|
+
print(f"Starting Workflow for task: '{task}' ...\n")
|
|
42
|
+
final_state = await wf.run(task, entry_point="research_node")
|
|
43
|
+
|
|
44
|
+
print("=== Final Pipeline State Output ===")
|
|
45
|
+
print(f"Total Steps Executed: {final_state.step}")
|
|
46
|
+
print(f"Researcher Output:\n{final_state.get('research_node_output')[:300]}...\n")
|
|
47
|
+
print(f"Coder Output:\n{final_state.get('coding_node_output')[:300]}...\n")
|
|
48
|
+
print(f"Reviewer Output:\n{final_state.get('review_node_output')}\n")
|
|
49
|
+
|
|
50
|
+
# Verify checkpoints
|
|
51
|
+
checkpoints = await checkpointer.list_checkpoints(final_state.session_id)
|
|
52
|
+
print(f"Checkpoints persisted: {checkpoints}")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
if __name__ == "__main__":
|
|
56
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Example 04: FastMCP / Model Context Protocol Integration."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from flowai import Agent
|
|
6
|
+
from flowai.mcp import MCPClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def main():
|
|
10
|
+
print("=== FlowAI MCP Integration Example ===")
|
|
11
|
+
|
|
12
|
+
# Initialize client to an MCP server (e.g. SQLite, GitHub, or custom FastMCP server)
|
|
13
|
+
mcp_client = MCPClient(
|
|
14
|
+
command="npx",
|
|
15
|
+
args=["-y", "@modelcontextprotocol/server-everything"],
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
print("Connecting to MCP server and discovering exposed tools...")
|
|
19
|
+
tools = await mcp_client.connect_and_discover_tools()
|
|
20
|
+
print(f"Discovered {len(tools)} MCP tools:")
|
|
21
|
+
for t in tools:
|
|
22
|
+
print(f" - {t.name}: {t.description}")
|
|
23
|
+
|
|
24
|
+
# Plug discovered MCP tools directly into a FlowAI agent
|
|
25
|
+
agent = Agent(
|
|
26
|
+
name="MCPWorker",
|
|
27
|
+
provider="nvidia",
|
|
28
|
+
model="deepseek-ai/deepseek-r1",
|
|
29
|
+
tools=tools,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
reply = await agent.run("List the tools you have and pick one to demonstrate.")
|
|
33
|
+
print(f"\n{agent.name}: {reply}")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if __name__ == "__main__":
|
|
37
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Example 05: Multi-agent Debate and Swarm Patterns."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from flowai import Agent, DebatePattern, Swarm
|
|
6
|
+
from flowai.providers.base import BaseProvider, ProviderResponse
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ScriptedProvider(BaseProvider):
|
|
10
|
+
"""Deterministic provider for simulation."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, script: dict[str, str]):
|
|
13
|
+
super().__init__(model="scripted")
|
|
14
|
+
self.script = script
|
|
15
|
+
|
|
16
|
+
async def generate(self, messages, tools=None, **kwargs):
|
|
17
|
+
last_msg = messages[-1].content
|
|
18
|
+
for k, reply in self.script.items():
|
|
19
|
+
if k in last_msg:
|
|
20
|
+
return ProviderResponse(content=reply)
|
|
21
|
+
return ProviderResponse(content="Default response.")
|
|
22
|
+
|
|
23
|
+
async def stream(self, messages, tools=None, **kwargs):
|
|
24
|
+
yield
|
|
25
|
+
|
|
26
|
+
async def embeddings(self, texts, model=None, **kwargs):
|
|
27
|
+
return []
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def test_debate():
|
|
31
|
+
print("=== Multi-Agent Debate Pattern ===")
|
|
32
|
+
provider_pro = ScriptedProvider({"IN FAVOR": "Remote work increases productivity and mental wellbeing."})
|
|
33
|
+
provider_con = ScriptedProvider({"AGAINST": "In-office work fosters spontaneous collaboration and mentorship."})
|
|
34
|
+
provider_judge = ScriptedProvider({"impartial judge": "Both presented strong points; hybrid mode is optimal."})
|
|
35
|
+
|
|
36
|
+
debater_a = Agent("ProRemote", provider=provider_pro)
|
|
37
|
+
debater_b = Agent("ProOffice", provider=provider_con)
|
|
38
|
+
judge = Agent("Judge", provider=provider_judge)
|
|
39
|
+
|
|
40
|
+
debate = DebatePattern(debater_a, debater_b, judge, rounds=1)
|
|
41
|
+
result = await debate.execute("Should companies mandate 5 days in-office?")
|
|
42
|
+
print("Debate Transcript:")
|
|
43
|
+
for entry in result["transcript"]:
|
|
44
|
+
print(f" - {entry}")
|
|
45
|
+
print(f"\nVerdict:\n{result['verdict']}\n")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def test_swarm():
|
|
49
|
+
print("=== Multi-Agent Swarm Dynamic Handoff ===")
|
|
50
|
+
triage_script = {
|
|
51
|
+
"Python": "HANDOFF_TO: PythonExpert Please write the Python implementation.",
|
|
52
|
+
"SQL": "HANDOFF_TO: SQLExpert Please optimize the SQL query.",
|
|
53
|
+
}
|
|
54
|
+
triage = Agent("TriageAgent", provider=ScriptedProvider(triage_script))
|
|
55
|
+
py_agent = Agent("PythonExpert", provider=ScriptedProvider({"write": "def solve(): return 42"}))
|
|
56
|
+
|
|
57
|
+
swarm = Swarm(agents=[triage, py_agent], initial_agent=triage)
|
|
58
|
+
result = await swarm.execute("I need a Python function to compute answer to life.")
|
|
59
|
+
print(f"Swarm Result:\n{result}")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
async def main():
|
|
63
|
+
await test_debate()
|
|
64
|
+
await test_swarm()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
asyncio.run(main())
|