nonoka 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.
Files changed (57) hide show
  1. nonoka-0.1.0/.gitignore +56 -0
  2. nonoka-0.1.0/Dockerfile +20 -0
  3. nonoka-0.1.0/LICENSE +21 -0
  4. nonoka-0.1.0/PKG-INFO +188 -0
  5. nonoka-0.1.0/README.md +157 -0
  6. nonoka-0.1.0/docker-compose.yml +26 -0
  7. nonoka-0.1.0/nonoka/__init__.py +58 -0
  8. nonoka-0.1.0/nonoka/agent.py +198 -0
  9. nonoka-0.1.0/nonoka/builder.py +186 -0
  10. nonoka-0.1.0/nonoka/cli/__init__.py +3 -0
  11. nonoka-0.1.0/nonoka/cli/main.py +141 -0
  12. nonoka-0.1.0/nonoka/config.py +99 -0
  13. nonoka-0.1.0/nonoka/events/__init__.py +0 -0
  14. nonoka-0.1.0/nonoka/events/bus.py +35 -0
  15. nonoka-0.1.0/nonoka/events/models.py +14 -0
  16. nonoka-0.1.0/nonoka/events/projector.py +104 -0
  17. nonoka-0.1.0/nonoka/events/store.py +84 -0
  18. nonoka-0.1.0/nonoka/llm/__init__.py +0 -0
  19. nonoka-0.1.0/nonoka/llm/base.py +34 -0
  20. nonoka-0.1.0/nonoka/llm/openai.py +63 -0
  21. nonoka-0.1.0/nonoka/policies.py +70 -0
  22. nonoka-0.1.0/nonoka/server/__init__.py +3 -0
  23. nonoka-0.1.0/nonoka/server/app.py +137 -0
  24. nonoka-0.1.0/nonoka/strategies/__init__.py +0 -0
  25. nonoka-0.1.0/nonoka/strategies/base.py +26 -0
  26. nonoka-0.1.0/nonoka/strategies/meta.py +52 -0
  27. nonoka-0.1.0/nonoka/strategies/react.py +65 -0
  28. nonoka-0.1.0/nonoka/strategies/task_loop.py +66 -0
  29. nonoka-0.1.0/nonoka/tools/__init__.py +0 -0
  30. nonoka-0.1.0/nonoka/tools/built_in/__init__.py +0 -0
  31. nonoka-0.1.0/nonoka/tools/decorator.py +86 -0
  32. nonoka-0.1.0/nonoka/tools/registry.py +36 -0
  33. nonoka-0.1.0/nonoka/tracing/__init__.py +5 -0
  34. nonoka-0.1.0/nonoka/tracing/base.py +8 -0
  35. nonoka-0.1.0/nonoka/tracing/console.py +54 -0
  36. nonoka-0.1.0/nonoka/tracing/json_file.py +21 -0
  37. nonoka-0.1.0/nonoka/workflow.py +51 -0
  38. nonoka-0.1.0/pyproject.toml +60 -0
  39. nonoka-0.1.0/tests/__init__.py +0 -0
  40. nonoka-0.1.0/tests/fixtures/__init__.py +0 -0
  41. nonoka-0.1.0/tests/integration/__init__.py +0 -0
  42. nonoka-0.1.0/tests/unit/__init__.py +0 -0
  43. nonoka-0.1.0/tests/unit/test_agent.py +313 -0
  44. nonoka-0.1.0/tests/unit/test_cli.py +53 -0
  45. nonoka-0.1.0/tests/unit/test_events.py +296 -0
  46. nonoka-0.1.0/tests/unit/test_llm.py +164 -0
  47. nonoka-0.1.0/tests/unit/test_policies.py +177 -0
  48. nonoka-0.1.0/tests/unit/test_projector.py +184 -0
  49. nonoka-0.1.0/tests/unit/test_server.py +78 -0
  50. nonoka-0.1.0/tests/unit/test_strategies.py +241 -0
  51. nonoka-0.1.0/tests/unit/test_tools.py +166 -0
  52. nonoka-0.1.0/tests/unit/test_tracing.py +111 -0
  53. nonoka-0.1.0/tests/unit/test_workflow.py +208 -0
  54. nonoka-0.1.0/tmp/agent.yaml +20 -0
  55. nonoka-0.1.0/tmp/test_sdk.py +99 -0
  56. nonoka-0.1.0/tmp/test_server.py +83 -0
  57. nonoka-0.1.0/uv.lock +833 -0
@@ -0,0 +1,56 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ .venv/
25
+ venv/
26
+ ENV/
27
+ env/
28
+
29
+ # IDEs
30
+ .vscode/
31
+ .idea/
32
+ *.swp
33
+ *.swo
34
+ *~
35
+
36
+ # Testing
37
+ .pytest_cache/
38
+ .coverage
39
+ htmlcov/
40
+
41
+ # SQLite
42
+ *.db
43
+ *.db-journal
44
+ *.db-wal
45
+ *.db-shm
46
+ *.sqlite
47
+ *.sqlite3
48
+
49
+ # Logs
50
+ *.log
51
+
52
+ # Local
53
+ .env
54
+ .agents/
55
+ .claude/
56
+ docs/
@@ -0,0 +1,20 @@
1
+ FROM python:3.12-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Install uv
6
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
7
+
8
+ # Copy package files
9
+ COPY pyproject.toml .
10
+ COPY README.md .
11
+ COPY nonoka/ ./nonoka/
12
+
13
+ # Install the package
14
+ RUN uv pip install --system -e .
15
+
16
+ # Expose FastAPI port
17
+ EXPOSE 8000
18
+
19
+ # Default command starts the server
20
+ CMD ["nonoka", "server", "--host", "0.0.0.0", "--port", "8000"]
nonoka-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fu Yu
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.
nonoka-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,188 @@
1
+ Metadata-Version: 2.4
2
+ Name: nonoka
3
+ Version: 0.1.0
4
+ Summary: Event-driven autonomous agent framework
5
+ Project-URL: Homepage, https://github.com/fyerfyer/nonoka
6
+ Project-URL: Repository, https://github.com/fyerfyer/nonoka
7
+ Project-URL: Documentation, https://github.com/fyerfyer/nonoka#readme
8
+ Author-email: Fu Yu <fyerfyer@126.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: agent,ai,autonomous,event-driven,llm
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.12
20
+ Requires-Dist: anyio>=4.13.0
21
+ Requires-Dist: fastapi>=0.136.1
22
+ Requires-Dist: openai>=2.38.0
23
+ Requires-Dist: pydantic-settings>=2.9.0
24
+ Requires-Dist: pydantic>=2.13.4
25
+ Requires-Dist: pyyaml>=6.0.2
26
+ Requires-Dist: rich>=15.0.0
27
+ Requires-Dist: structlog>=25.5.0
28
+ Requires-Dist: typer>=0.25.1
29
+ Requires-Dist: uvicorn[standard]>=0.47.0
30
+ Description-Content-Type: text/markdown
31
+
32
+ # Nonoka Agent
33
+
34
+ An event-driven autonomous agent framework built with FastAPI, Pydantic, and modern Python async patterns.
35
+
36
+ ## Features
37
+
38
+ - **Event-driven core** — all state changes are events, fully observable
39
+ - **Pluggable strategies** — meta, ReAct, task-loop with easy custom strategy support
40
+ - **First-class tools** — Pydantic schema generation via decorators
41
+ - **Config-driven** — run agents from YAML or environment variables
42
+ - **CLI + Server** — interactive chat, config-based runs, or FastAPI HTTP API
43
+ - **Structured tracing** — console and JSON file tracers built in
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ pip install nonoka
49
+ ```
50
+
51
+ Or with uv:
52
+
53
+ ```bash
54
+ uv add nonoka
55
+ ```
56
+
57
+ ## Quick Start
58
+
59
+ ### SDK — Write Python
60
+
61
+ ```python
62
+ import asyncio
63
+ import nonoka
64
+
65
+ async def main():
66
+ # From config file
67
+ agent = await nonoka.Agent.from_config("agent.yaml")
68
+ state = await agent.run("What is the capital of France?")
69
+ print(state.tool_results[0].output)
70
+
71
+ # Or programmatically with AgentBuilder
72
+ agent = await (
73
+ nonoka.AgentBuilder()
74
+ .with_llm("openai", api_key="sk-...", model="deepseek-chat", base_url="https://api.deepseek.com")
75
+ .with_strategy("react", nonoka.ReActStrategy())
76
+ .with_tool(my_search_tool)
77
+ .build()
78
+ )
79
+ state = await agent.run("Research the latest AI news")
80
+
81
+ asyncio.run(main())
82
+ ```
83
+
84
+ ### CLI — Run without code
85
+
86
+ ```bash
87
+ # Interactive chat
88
+ nonoka chat --strategy react --llm-model deepseek-chat
89
+
90
+ # Run from config
91
+ nonoka run agent.yaml --task "What is 2 + 2?"
92
+
93
+ # Start server
94
+ nonoka server --host 0.0.0.0 --port 8000
95
+ ```
96
+
97
+ ### Docker
98
+
99
+ ```bash
100
+ docker build -t nonoka .
101
+ docker run -e OPENAI_API_KEY=sk-... -p 8000:8000 nonoka
102
+ ```
103
+
104
+ Or with Docker Compose:
105
+
106
+ ```bash
107
+ docker compose up
108
+ ```
109
+
110
+ ## Configuration
111
+
112
+ Create an `agent.yaml`:
113
+
114
+ ```yaml
115
+ llm:
116
+ provider: openai
117
+ api_key: ${OPENAI_API_KEY}
118
+ model: deepseek-chat
119
+ base_url: https://api.deepseek.com
120
+
121
+ default_strategy: react
122
+
123
+ store:
124
+ type: sqlite
125
+ db_path: ":memory:"
126
+
127
+ tracers:
128
+ - type: console
129
+
130
+ policies:
131
+ - type: retry
132
+ max_retries: 3
133
+ - type: timeout
134
+ timeout_seconds: 60
135
+ ```
136
+
137
+ Environment variables (NONOKA_ prefix):
138
+
139
+ | Variable | Description |
140
+ |----------|-------------|
141
+ | `NONOKA_LLM__API_KEY` | LLM API key |
142
+ | `NONOKA_LLM__MODEL` | Model name (e.g. gpt-4, deepseek-chat) |
143
+ | `NONOKA_LLM__BASE_URL` | Custom base URL for OpenAI-compatible APIs |
144
+ | `NONOKA_DEFAULT_STRATEGY` | Default strategy (meta, react, task_loop) |
145
+
146
+ ## Writing Custom Tools
147
+
148
+ ```python
149
+ import nonoka
150
+
151
+ @nonoka.tool
152
+ async def search(query: str, limit: int = 10) -> nonoka.TaskResult[list[str]]:
153
+ """Search the web for information."""
154
+ results = ["result1", "result2"]
155
+ return nonoka.TaskResult.ok(results)
156
+
157
+ agent = await nonoka.AgentBuilder().with_tool(search).build()
158
+ ```
159
+
160
+ ## HTTP API
161
+
162
+ When running `nonoka server`:
163
+
164
+ | Endpoint | Description |
165
+ |----------|-------------|
166
+ | `POST /agents` | Create and start an agent |
167
+ | `GET /agents/{id}` | Get agent state |
168
+ | `GET /agents/{id}/events` | SSE stream of events |
169
+ | `GET /agents/{id}/trace` | Full event log |
170
+ | `POST /agents/{id}/cancel` | Cancel agent |
171
+
172
+ ## Architecture
173
+
174
+ ```
175
+ User Input -> EventBus.publish(TaskCreated)
176
+ -> MetaStrategy.handle() -> StrategySelected
177
+ -> SelectedStrategy.handle() -> Actions
178
+ -> Agent.execute(actions)
179
+ -> call_tool -> ToolExecutor -> ToolCompleted/ToolFailed
180
+ -> emit_event -> EventBus.publish()
181
+ -> complete -> AgentCompleted
182
+ -> StateProjector.project(events) -> AgentState
183
+ -> Tracer.on_event(event)
184
+ ```
185
+
186
+ ## License
187
+
188
+ MIT
nonoka-0.1.0/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # Nonoka Agent
2
+
3
+ An event-driven autonomous agent framework built with FastAPI, Pydantic, and modern Python async patterns.
4
+
5
+ ## Features
6
+
7
+ - **Event-driven core** — all state changes are events, fully observable
8
+ - **Pluggable strategies** — meta, ReAct, task-loop with easy custom strategy support
9
+ - **First-class tools** — Pydantic schema generation via decorators
10
+ - **Config-driven** — run agents from YAML or environment variables
11
+ - **CLI + Server** — interactive chat, config-based runs, or FastAPI HTTP API
12
+ - **Structured tracing** — console and JSON file tracers built in
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pip install nonoka
18
+ ```
19
+
20
+ Or with uv:
21
+
22
+ ```bash
23
+ uv add nonoka
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ### SDK — Write Python
29
+
30
+ ```python
31
+ import asyncio
32
+ import nonoka
33
+
34
+ async def main():
35
+ # From config file
36
+ agent = await nonoka.Agent.from_config("agent.yaml")
37
+ state = await agent.run("What is the capital of France?")
38
+ print(state.tool_results[0].output)
39
+
40
+ # Or programmatically with AgentBuilder
41
+ agent = await (
42
+ nonoka.AgentBuilder()
43
+ .with_llm("openai", api_key="sk-...", model="deepseek-chat", base_url="https://api.deepseek.com")
44
+ .with_strategy("react", nonoka.ReActStrategy())
45
+ .with_tool(my_search_tool)
46
+ .build()
47
+ )
48
+ state = await agent.run("Research the latest AI news")
49
+
50
+ asyncio.run(main())
51
+ ```
52
+
53
+ ### CLI — Run without code
54
+
55
+ ```bash
56
+ # Interactive chat
57
+ nonoka chat --strategy react --llm-model deepseek-chat
58
+
59
+ # Run from config
60
+ nonoka run agent.yaml --task "What is 2 + 2?"
61
+
62
+ # Start server
63
+ nonoka server --host 0.0.0.0 --port 8000
64
+ ```
65
+
66
+ ### Docker
67
+
68
+ ```bash
69
+ docker build -t nonoka .
70
+ docker run -e OPENAI_API_KEY=sk-... -p 8000:8000 nonoka
71
+ ```
72
+
73
+ Or with Docker Compose:
74
+
75
+ ```bash
76
+ docker compose up
77
+ ```
78
+
79
+ ## Configuration
80
+
81
+ Create an `agent.yaml`:
82
+
83
+ ```yaml
84
+ llm:
85
+ provider: openai
86
+ api_key: ${OPENAI_API_KEY}
87
+ model: deepseek-chat
88
+ base_url: https://api.deepseek.com
89
+
90
+ default_strategy: react
91
+
92
+ store:
93
+ type: sqlite
94
+ db_path: ":memory:"
95
+
96
+ tracers:
97
+ - type: console
98
+
99
+ policies:
100
+ - type: retry
101
+ max_retries: 3
102
+ - type: timeout
103
+ timeout_seconds: 60
104
+ ```
105
+
106
+ Environment variables (NONOKA_ prefix):
107
+
108
+ | Variable | Description |
109
+ |----------|-------------|
110
+ | `NONOKA_LLM__API_KEY` | LLM API key |
111
+ | `NONOKA_LLM__MODEL` | Model name (e.g. gpt-4, deepseek-chat) |
112
+ | `NONOKA_LLM__BASE_URL` | Custom base URL for OpenAI-compatible APIs |
113
+ | `NONOKA_DEFAULT_STRATEGY` | Default strategy (meta, react, task_loop) |
114
+
115
+ ## Writing Custom Tools
116
+
117
+ ```python
118
+ import nonoka
119
+
120
+ @nonoka.tool
121
+ async def search(query: str, limit: int = 10) -> nonoka.TaskResult[list[str]]:
122
+ """Search the web for information."""
123
+ results = ["result1", "result2"]
124
+ return nonoka.TaskResult.ok(results)
125
+
126
+ agent = await nonoka.AgentBuilder().with_tool(search).build()
127
+ ```
128
+
129
+ ## HTTP API
130
+
131
+ When running `nonoka server`:
132
+
133
+ | Endpoint | Description |
134
+ |----------|-------------|
135
+ | `POST /agents` | Create and start an agent |
136
+ | `GET /agents/{id}` | Get agent state |
137
+ | `GET /agents/{id}/events` | SSE stream of events |
138
+ | `GET /agents/{id}/trace` | Full event log |
139
+ | `POST /agents/{id}/cancel` | Cancel agent |
140
+
141
+ ## Architecture
142
+
143
+ ```
144
+ User Input -> EventBus.publish(TaskCreated)
145
+ -> MetaStrategy.handle() -> StrategySelected
146
+ -> SelectedStrategy.handle() -> Actions
147
+ -> Agent.execute(actions)
148
+ -> call_tool -> ToolExecutor -> ToolCompleted/ToolFailed
149
+ -> emit_event -> EventBus.publish()
150
+ -> complete -> AgentCompleted
151
+ -> StateProjector.project(events) -> AgentState
152
+ -> Tracer.on_event(event)
153
+ ```
154
+
155
+ ## License
156
+
157
+ MIT
@@ -0,0 +1,26 @@
1
+ services:
2
+ redis:
3
+ image: redis:7-alpine
4
+ ports:
5
+ - "6379:6379"
6
+ healthcheck:
7
+ test: ["CMD", "redis-cli", "ping"]
8
+ interval: 5s
9
+ timeout: 3s
10
+ retries: 5
11
+
12
+ nonoka:
13
+ build: .
14
+ ports:
15
+ - "8000:8000"
16
+ environment:
17
+ - NONOKA_LLM__API_KEY=${OPENAI_API_KEY}
18
+ - NONOKA_LLM__MODEL=${NONOKA_LLM_MODEL:-deepseek-chat}
19
+ - NONOKA_LLM__BASE_URL=${NONOKA_LLM_BASE_URL:-https://api.deepseek.com}
20
+ env_file:
21
+ - .env
22
+ depends_on:
23
+ redis:
24
+ condition: service_healthy
25
+ volumes:
26
+ - ./agent.yaml:/app/agent.yaml:ro
@@ -0,0 +1,58 @@
1
+ from nonoka.agent import Agent
2
+ from nonoka.events.models import Event
3
+ from nonoka.events.bus import EventBus, InMemoryEventBus
4
+ from nonoka.events.store import EventStore, SQLiteEventStore
5
+ from nonoka.events.projector import AgentState, StateProjector
6
+ from nonoka.policies import Policy, PolicyDecision, PolicyContext, RetryPolicy, TimeoutPolicy
7
+ from nonoka.strategies.base import Action, Strategy, StrategyContext
8
+ from nonoka.strategies.meta import MetaStrategy
9
+ from nonoka.strategies.react import ReActStrategy
10
+ from nonoka.strategies.task_loop import TaskLoopStrategy
11
+ from nonoka.tools.decorator import TaskResult, tool
12
+ from nonoka.tools.registry import Tool, ToolRegistry
13
+ from nonoka.tracing.base import Tracer
14
+ from nonoka.tracing.console import ConsoleTracer
15
+ from nonoka.tracing.json_file import JsonFileTracer
16
+ from nonoka.builder import AgentBuilder, from_config
17
+ from nonoka.config import AgentConfig, LLMConfig, PolicyConfig, StoreConfig, TracerConfig
18
+ from nonoka.workflow import task, Workflow
19
+
20
+ __version__ = "0.1.0"
21
+
22
+ __all__ = [
23
+ "Agent",
24
+ "Event",
25
+ "EventBus",
26
+ "InMemoryEventBus",
27
+ "EventStore",
28
+ "SQLiteEventStore",
29
+ "AgentState",
30
+ "StateProjector",
31
+ "Policy",
32
+ "PolicyDecision",
33
+ "PolicyContext",
34
+ "RetryPolicy",
35
+ "TimeoutPolicy",
36
+ "Action",
37
+ "Strategy",
38
+ "StrategyContext",
39
+ "MetaStrategy",
40
+ "ReActStrategy",
41
+ "TaskLoopStrategy",
42
+ "TaskResult",
43
+ "tool",
44
+ "Tool",
45
+ "ToolRegistry",
46
+ "Tracer",
47
+ "ConsoleTracer",
48
+ "JsonFileTracer",
49
+ "AgentBuilder",
50
+ "from_config",
51
+ "AgentConfig",
52
+ "LLMConfig",
53
+ "PolicyConfig",
54
+ "StoreConfig",
55
+ "TracerConfig",
56
+ "task",
57
+ "Workflow",
58
+ ]