jac-coder 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.
- jac_coder-0.1.0/PKG-INFO +9 -0
- jac_coder-0.1.0/README.md +141 -0
- jac_coder-0.1.0/jac_coder/__init__.jac +0 -0
- jac_coder-0.1.0/jac_coder/api.jac +82 -0
- jac_coder-0.1.0/jac_coder/cli_entry.py +25 -0
- jac_coder-0.1.0/jac_coder/config.jac +36 -0
- jac_coder-0.1.0/jac_coder/context.jac +17 -0
- jac_coder-0.1.0/jac_coder/data/examples/ai_agent.md +90 -0
- jac_coder-0.1.0/jac_coder/data/examples/blog_app.md +386 -0
- jac_coder-0.1.0/jac_coder/data/examples/core_patterns.md +321 -0
- jac_coder-0.1.0/jac_coder/data/examples/todo_app.md +321 -0
- jac_coder-0.1.0/jac_coder/data/reference/ai.md +131 -0
- jac_coder-0.1.0/jac_coder/data/reference/backend.md +215 -0
- jac_coder-0.1.0/jac_coder/data/reference/frontend.md +271 -0
- jac_coder-0.1.0/jac_coder/data/reference/osp.md +229 -0
- jac_coder-0.1.0/jac_coder/data/reference/pitfalls.md +141 -0
- jac_coder-0.1.0/jac_coder/data/reference/syntax.md +159 -0
- jac_coder-0.1.0/jac_coder/data/rules/core_jac.md +559 -0
- jac_coder-0.1.0/jac_coder/data/rules/fullstack.md +362 -0
- jac_coder-0.1.0/jac_coder/data/rules/workflow.md +88 -0
- jac_coder-0.1.0/jac_coder/events.jac +110 -0
- jac_coder-0.1.0/jac_coder/impl/api.impl.jac +399 -0
- jac_coder-0.1.0/jac_coder/impl/config.impl.jac +163 -0
- jac_coder-0.1.0/jac_coder/impl/context.impl.jac +117 -0
- jac_coder-0.1.0/jac_coder/impl/mcp_manager.impl.jac +380 -0
- jac_coder-0.1.0/jac_coder/impl/memory.impl.jac +247 -0
- jac_coder-0.1.0/jac_coder/impl/nodes.impl.jac +259 -0
- jac_coder-0.1.0/jac_coder/impl/permission.impl.jac +62 -0
- jac_coder-0.1.0/jac_coder/impl/walkers.impl.jac +298 -0
- jac_coder-0.1.0/jac_coder/mcp_manager.jac +35 -0
- jac_coder-0.1.0/jac_coder/memory.jac +15 -0
- jac_coder-0.1.0/jac_coder/nodes.jac +306 -0
- jac_coder-0.1.0/jac_coder/permission.jac +19 -0
- jac_coder-0.1.0/jac_coder/serve_entry.jac +30 -0
- jac_coder-0.1.0/jac_coder/server.jac +324 -0
- jac_coder-0.1.0/jac_coder/tool/__init__.jac +17 -0
- jac_coder-0.1.0/jac_coder/tool/checked.jac +10 -0
- jac_coder-0.1.0/jac_coder/tool/delegation.jac +23 -0
- jac_coder-0.1.0/jac_coder/tool/filesystem.jac +25 -0
- jac_coder-0.1.0/jac_coder/tool/git.jac +18 -0
- jac_coder-0.1.0/jac_coder/tool/guarded.jac +23 -0
- jac_coder-0.1.0/jac_coder/tool/impl/checked.impl.jac +38 -0
- jac_coder-0.1.0/jac_coder/tool/impl/delegation.impl.jac +157 -0
- jac_coder-0.1.0/jac_coder/tool/impl/filesystem.impl.jac +781 -0
- jac_coder-0.1.0/jac_coder/tool/impl/git.impl.jac +115 -0
- jac_coder-0.1.0/jac_coder/tool/impl/guarded.impl.jac +72 -0
- jac_coder-0.1.0/jac_coder/tool/impl/jac_analyzer.impl.jac +593 -0
- jac_coder-0.1.0/jac_coder/tool/impl/jac_docs.impl.jac +136 -0
- jac_coder-0.1.0/jac_coder/tool/impl/jac_tools.impl.jac +79 -0
- jac_coder-0.1.0/jac_coder/tool/impl/mcp.impl.jac +32 -0
- jac_coder-0.1.0/jac_coder/tool/impl/preview.impl.jac +233 -0
- jac_coder-0.1.0/jac_coder/tool/impl/question.impl.jac +29 -0
- jac_coder-0.1.0/jac_coder/tool/impl/scaffold.impl.jac +231 -0
- jac_coder-0.1.0/jac_coder/tool/impl/search.impl.jac +85 -0
- jac_coder-0.1.0/jac_coder/tool/impl/shell.impl.jac +89 -0
- jac_coder-0.1.0/jac_coder/tool/impl/task.impl.jac +12 -0
- jac_coder-0.1.0/jac_coder/tool/impl/think.impl.jac +4 -0
- jac_coder-0.1.0/jac_coder/tool/impl/todo.impl.jac +58 -0
- jac_coder-0.1.0/jac_coder/tool/impl/validate.impl.jac +236 -0
- jac_coder-0.1.0/jac_coder/tool/impl/web.impl.jac +91 -0
- jac_coder-0.1.0/jac_coder/tool/jac_analyzer.jac +21 -0
- jac_coder-0.1.0/jac_coder/tool/jac_docs.jac +9 -0
- jac_coder-0.1.0/jac_coder/tool/jac_tools.jac +11 -0
- jac_coder-0.1.0/jac_coder/tool/mcp.jac +17 -0
- jac_coder-0.1.0/jac_coder/tool/preview.jac +31 -0
- jac_coder-0.1.0/jac_coder/tool/question.jac +7 -0
- jac_coder-0.1.0/jac_coder/tool/scaffold.jac +10 -0
- jac_coder-0.1.0/jac_coder/tool/search.jac +14 -0
- jac_coder-0.1.0/jac_coder/tool/shell.jac +12 -0
- jac_coder-0.1.0/jac_coder/tool/task.jac +9 -0
- jac_coder-0.1.0/jac_coder/tool/think.jac +5 -0
- jac_coder-0.1.0/jac_coder/tool/todo.jac +12 -0
- jac_coder-0.1.0/jac_coder/tool/validate.jac +11 -0
- jac_coder-0.1.0/jac_coder/tool/vision.jac +17 -0
- jac_coder-0.1.0/jac_coder/tool/web.jac +10 -0
- jac_coder-0.1.0/jac_coder/util/__init__.jac +18 -0
- jac_coder-0.1.0/jac_coder/util/colors.jac +20 -0
- jac_coder-0.1.0/jac_coder/util/impl/sandbox.impl.jac +38 -0
- jac_coder-0.1.0/jac_coder/util/impl/tool_output.impl.jac +208 -0
- jac_coder-0.1.0/jac_coder/util/sandbox.jac +8 -0
- jac_coder-0.1.0/jac_coder/util/tool_output.jac +29 -0
- jac_coder-0.1.0/jac_coder/walkers.jac +67 -0
- jac_coder-0.1.0/jac_coder.egg-info/PKG-INFO +9 -0
- jac_coder-0.1.0/jac_coder.egg-info/SOURCES.txt +95 -0
- jac_coder-0.1.0/jac_coder.egg-info/dependency_links.txt +1 -0
- jac_coder-0.1.0/jac_coder.egg-info/entry_points.txt +3 -0
- jac_coder-0.1.0/jac_coder.egg-info/requires.txt +4 -0
- jac_coder-0.1.0/jac_coder.egg-info/top_level.txt +1 -0
- jac_coder-0.1.0/pyproject.toml +29 -0
- jac_coder-0.1.0/setup.cfg +4 -0
- jac_coder-0.1.0/tests/test_context.py +53 -0
- jac_coder-0.1.0/tests/test_events.py +40 -0
- jac_coder-0.1.0/tests/test_graph.py +33 -0
- jac_coder-0.1.0/tests/test_interact.py +40 -0
- jac_coder-0.1.0/tests/test_memory.py +53 -0
- jac_coder-0.1.0/tests/test_selfcorrect.py +72 -0
- jac_coder-0.1.0/tests/test_tools.py +45 -0
jac_coder-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# JacCoder
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
AI coding agent for the Jaseci stack, built entirely in [Jac](https://github.com/jaseci-labs/jaseci) using Object-Spatial Programming. Features an orchestrator-worker architecture with compiler-level Jac Intelligence, self-correcting code writes, and in-process SubAgent delegation.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Orchestrator-Worker Architecture** - MainAgent handles tasks directly or delegates to focused SubAgents (WorkerRunner / ExplorerRunner)
|
|
14
|
+
- **Jac Intelligence** - compiler-level AST analysis via jaclang (`analyze_project`, `find_symbol`)
|
|
15
|
+
- **22 Built-in Tools** - filesystem, search, git, shell, web, Jac compiler, scaffolding, delegation
|
|
16
|
+
- **Self-Correcting Writes** - automatic `jac check` on `.jac` file writes with error feedback
|
|
17
|
+
- **In-Process SubAgents** - no subprocess overhead, shared event system, capability-scoped tools
|
|
18
|
+
- **Multi-Provider LLM** - 100+ models via [byllm](https://github.com/jaseci-labs/byllm)
|
|
19
|
+
- **Public API** - clean interface for external apps (JacBuilder, IDE extensions)
|
|
20
|
+
- **Context Management** - smart compaction keeps conversations under token budget
|
|
21
|
+
|
|
22
|
+
## Prerequisites
|
|
23
|
+
|
|
24
|
+
- Python 3.12+
|
|
25
|
+
- jaclang, byllm, jac-super
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install jaclang byllm jac-super python-dotenv
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Set API key
|
|
35
|
+
export OPENAI_API_KEY="sk-..."
|
|
36
|
+
|
|
37
|
+
# Interactive REPL
|
|
38
|
+
jac cli.jac
|
|
39
|
+
|
|
40
|
+
# Single prompt (non-interactive)
|
|
41
|
+
jac cli.jac run "build a hello world jac app at /tmp/myapp"
|
|
42
|
+
|
|
43
|
+
# Resume a session
|
|
44
|
+
jac cli.jac session <id-prefix>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Architecture
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
Root → Session → MainAgent
|
|
51
|
+
├── handles simple tasks directly (read, search, edit, git)
|
|
52
|
+
└── spawn_agent() → WorkerRunner (read+write) or ExplorerRunner (read-only)
|
|
53
|
+
└── runs in-process, returns result to MainAgent
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- **MainAgent (node)** - orchestrator with 25 tools. Handles simple tasks directly, delegates complex work.
|
|
57
|
+
- **WorkerRunner (obj)** - in-process SubAgent with 18 tools (can write/edit/run). Full Jac syntax rules.
|
|
58
|
+
- **ExplorerRunner (obj)** - in-process SubAgent with 10 tools (read-only). For investigation tasks.
|
|
59
|
+
- **Session (node)** - persistent chat state, history, active files, errors.
|
|
60
|
+
- **ProjectMemory (node)** - AST-derived codebase knowledge (nodes, walkers, edges, imports).
|
|
61
|
+
|
|
62
|
+
## Tools
|
|
63
|
+
|
|
64
|
+
| Tool | Description |
|
|
65
|
+
|------|-------------|
|
|
66
|
+
| `read_file` | Read files with line numbers and pagination |
|
|
67
|
+
| `write_code` | Write file with auto `jac_check` validation |
|
|
68
|
+
| `edit_code` | Find-and-replace with auto `jac_check` validation |
|
|
69
|
+
| `find_files` | Find files by glob pattern |
|
|
70
|
+
| `grep_search` | Regex search across files |
|
|
71
|
+
| `list_files` | List directory contents |
|
|
72
|
+
| `run_command` | Execute shell commands (permission-guarded) |
|
|
73
|
+
| `jac_check` | Type-check `.jac` files |
|
|
74
|
+
| `jac_run` | Run `.jac` files |
|
|
75
|
+
| `jac_docs` | Search Jac language reference and examples |
|
|
76
|
+
| `analyze_project` | Full AST analysis of a Jac project |
|
|
77
|
+
| `find_symbol` | Find symbol definitions, usages, and imports |
|
|
78
|
+
| `git_status` | Show working tree status |
|
|
79
|
+
| `git_diff` | Show uncommitted changes |
|
|
80
|
+
| `git_log` | Show recent commit history |
|
|
81
|
+
| `git_commit` | Stage and commit changes |
|
|
82
|
+
| `think` | Explicit reasoning step before acting |
|
|
83
|
+
| `spawn_agent` | Delegate task to a SubAgent (worker/explorer) |
|
|
84
|
+
| `web_fetch` | Fetch URL content |
|
|
85
|
+
| `web_search` | Web search via DuckDuckGo |
|
|
86
|
+
| `scaffold_project` | Generate project templates |
|
|
87
|
+
| `ask_question` | Prompt user for input |
|
|
88
|
+
| `update_todos` | Track multi-step task progress |
|
|
89
|
+
|
|
90
|
+
## Public API
|
|
91
|
+
|
|
92
|
+
External apps import only from `jac_coder.api`:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from jac_coder.api import initialize, create_session, chat, close_session
|
|
96
|
+
|
|
97
|
+
initialize("web")
|
|
98
|
+
session = create_session("/path/to/project", title="My App")
|
|
99
|
+
result = chat(session["session_id"], "build a calculator")
|
|
100
|
+
print(result["response"])
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Configuration
|
|
104
|
+
|
|
105
|
+
Config sources (highest priority first):
|
|
106
|
+
|
|
107
|
+
1. Environment: `MODEL`, `TEMPERATURE`, `MAX_TOKENS`, `MAX_REACT_ITERATIONS`
|
|
108
|
+
2. Project: `./jaccoder.json`
|
|
109
|
+
3. Global: `~/.jaccoder/config.json`
|
|
110
|
+
|
|
111
|
+
## Testing
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
python -m pytest tests/ -v # 58 tests
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Project Structure
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
jac-code/
|
|
121
|
+
├── cli.jac # CLI entry point (REPL + subcommands)
|
|
122
|
+
├── jac_coder/ # Core package
|
|
123
|
+
│ ├── api.jac # Public API (the only module external apps import)
|
|
124
|
+
│ ├── nodes.jac # MainAgent, WorkerRunner, ExplorerRunner, Session, ProjectMemory
|
|
125
|
+
│ ├── walkers.jac # Walker definitions (interact, new_session, etc.)
|
|
126
|
+
│ ├── config.jac # Multi-source config + LLM init
|
|
127
|
+
│ ├── context.jac # Smart context compaction
|
|
128
|
+
│ ├── events.jac # Tool event system + turn summaries
|
|
129
|
+
│ ├── memory.jac # AST-based project memory
|
|
130
|
+
│ ├── permission.jac # Permission rule engine
|
|
131
|
+
│ ├── tool/ # 22 tools organized by domain
|
|
132
|
+
│ │ ├── delegation.jac # spawn_agent (in-process SubAgent execution)
|
|
133
|
+
│ │ ├── think.jac # Chain-of-thought reasoning
|
|
134
|
+
│ │ ├── git.jac # Git operations
|
|
135
|
+
│ │ ├── jac_analyzer.jac # AST-based Jac Intelligence
|
|
136
|
+
│ │ └── ...
|
|
137
|
+
│ ├── data/ # Jac syntax rules (build_rules, client_rules, server_rules)
|
|
138
|
+
│ └── impl/ # Implementation files
|
|
139
|
+
├── tests/ # 58 tests (pytest + jac)
|
|
140
|
+
└── docs/ # Architecture, roadmap, progress
|
|
141
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""JacCoder public API."""
|
|
2
|
+
|
|
3
|
+
import os;
|
|
4
|
+
import sys;
|
|
5
|
+
|
|
6
|
+
import from datetime { datetime }
|
|
7
|
+
import from jac_coder.config {
|
|
8
|
+
get_config,
|
|
9
|
+
set_model as _set_model,
|
|
10
|
+
get_model as _get_model
|
|
11
|
+
}
|
|
12
|
+
import from jac_coder.util.tool_output { tool_end }
|
|
13
|
+
import from jac_coder.permission { permission_engine }
|
|
14
|
+
import from jac_coder.util.sandbox { set_sandbox_root }
|
|
15
|
+
import from jac_coder.context { build_context, ContextConfig }
|
|
16
|
+
import from jac_coder.walkers { new_session, ensure_main_agent, _consume_llm_stream }
|
|
17
|
+
import from jac_coder.mcp_manager {
|
|
18
|
+
mcp_add_server,
|
|
19
|
+
mcp_remove_server,
|
|
20
|
+
mcp_list_servers,
|
|
21
|
+
mcp_get_tools,
|
|
22
|
+
mcp_register_builtin
|
|
23
|
+
}
|
|
24
|
+
import from jac_coder.memory {
|
|
25
|
+
find_or_create_memory,
|
|
26
|
+
_init_memory,
|
|
27
|
+
update_memory_from_session
|
|
28
|
+
}
|
|
29
|
+
import from jac_coder.nodes { Session, MainAgent }
|
|
30
|
+
import from jac_coder.events {
|
|
31
|
+
register_event_callback,
|
|
32
|
+
clear_event_callbacks,
|
|
33
|
+
reset_steps,
|
|
34
|
+
start_turn,
|
|
35
|
+
emit_turn_summary,
|
|
36
|
+
emit_event
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# Thread-safe session registry — graph root() is per-request in jac-cloud,
|
|
41
|
+
# so background threads can't find sessions via [root()-->]. This dict
|
|
42
|
+
# is module-level and accessible from any thread.
|
|
43
|
+
glob _session_registry: dict = {};
|
|
44
|
+
|
|
45
|
+
"""Initialize jac-coder."""
|
|
46
|
+
def initialize(mode: str = "web") -> None;
|
|
47
|
+
|
|
48
|
+
"""Create a new session."""
|
|
49
|
+
def create_session(directory: str, title: str = "", agent: str = "main") -> dict;
|
|
50
|
+
|
|
51
|
+
"""Send a message and get a response."""
|
|
52
|
+
def chat(
|
|
53
|
+
session_id: str,
|
|
54
|
+
message: str,
|
|
55
|
+
directory: str = "",
|
|
56
|
+
on_event: Any = None,
|
|
57
|
+
mode: str = "full",
|
|
58
|
+
agent_context: str = ""
|
|
59
|
+
) -> dict;
|
|
60
|
+
|
|
61
|
+
def list_sessions() -> list;
|
|
62
|
+
|
|
63
|
+
def get_session(session_id: str) -> dict;
|
|
64
|
+
|
|
65
|
+
def close_session(session_id: str) -> dict;
|
|
66
|
+
|
|
67
|
+
# --- Model management API ---
|
|
68
|
+
"""Switch the active LLM model."""
|
|
69
|
+
def api_set_model(model: str) -> dict;
|
|
70
|
+
|
|
71
|
+
"""Get current model info."""
|
|
72
|
+
def api_get_model() -> dict;
|
|
73
|
+
|
|
74
|
+
# --- MCP management API ---
|
|
75
|
+
"""Add or update an MCP server."""
|
|
76
|
+
def api_mcp_add(name: str, config: dict) -> dict;
|
|
77
|
+
|
|
78
|
+
"""Remove an MCP server."""
|
|
79
|
+
def api_mcp_remove(name: str) -> dict;
|
|
80
|
+
|
|
81
|
+
"""List all configured MCP servers."""
|
|
82
|
+
def api_mcp_list() -> list;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""CLI entry point for `jac-coder` command."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
# Find the project root (where cli.jac lives)
|
|
10
|
+
pkg_dir = os.path.dirname(os.path.abspath(__file__))
|
|
11
|
+
project_root = os.path.dirname(pkg_dir)
|
|
12
|
+
cli_jac = os.path.join(project_root, "cli.jac")
|
|
13
|
+
|
|
14
|
+
if not os.path.exists(cli_jac):
|
|
15
|
+
print("Error: cli.jac not found. Run from the jac-coder project directory.")
|
|
16
|
+
sys.exit(1)
|
|
17
|
+
|
|
18
|
+
# Set JACCODER_ROOT so tools can find data/ directory regardless of CWD
|
|
19
|
+
os.environ["JACCODER_ROOT"] = project_root
|
|
20
|
+
|
|
21
|
+
sys.exit(subprocess.call(["jac", cli_jac] + sys.argv[1:]))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
main()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import os;
|
|
2
|
+
import json;
|
|
3
|
+
|
|
4
|
+
import from pathlib { Path }
|
|
5
|
+
import from dotenv { load_dotenv }
|
|
6
|
+
import from byllm.lib { Model }
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
obj JacCoderConfig {
|
|
10
|
+
has default_model: str = "gpt-5.2-2025-12-11",
|
|
11
|
+
temperature: float = 0.2,
|
|
12
|
+
max_tokens: int = 8192,
|
|
13
|
+
max_react_iterations: int = 30,
|
|
14
|
+
project_dir: str = "";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
glob _config: JacCoderConfig = JacCoderConfig();
|
|
18
|
+
|
|
19
|
+
# --- Config ---
|
|
20
|
+
def get_home_dir() -> str;
|
|
21
|
+
def _merge_from_file(config: JacCoderConfig, path: str) -> None;
|
|
22
|
+
def load_config(project_dir: str = "") -> JacCoderConfig;
|
|
23
|
+
def get_config() -> JacCoderConfig;
|
|
24
|
+
def get_data_dir() -> str;
|
|
25
|
+
|
|
26
|
+
# --- Model management ---
|
|
27
|
+
def set_model(model: str) -> dict;
|
|
28
|
+
def get_model() -> dict;
|
|
29
|
+
|
|
30
|
+
with entry {
|
|
31
|
+
load_dotenv(override=True);
|
|
32
|
+
load_config();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
glob model_name: str = _config.default_model;
|
|
36
|
+
glob llm = Model(model_name=model_name);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
obj ContextConfig {
|
|
2
|
+
has token_budget: int = 20000;
|
|
3
|
+
has recent_window: int = 8;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def estimate_tokens(messages: list[dict]) -> int;
|
|
8
|
+
|
|
9
|
+
def needs_compaction(messages: list[dict], budget: int = 20000) -> bool;
|
|
10
|
+
|
|
11
|
+
def build_context(
|
|
12
|
+
chat_history: list[dict],
|
|
13
|
+
active_files: list[str] = [],
|
|
14
|
+
pending_errors: list[str] = [],
|
|
15
|
+
config: ContextConfig | None = None,
|
|
16
|
+
project_summary: str = ""
|
|
17
|
+
) -> list[dict];
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Example: AI Agent with LLM Routing
|
|
2
|
+
|
|
3
|
+
Uses `by llm()` for intelligent routing, `sem` annotations for prompts, walker graph traversal.
|
|
4
|
+
|
|
5
|
+
## Backend: agent.jac — Declarations
|
|
6
|
+
|
|
7
|
+
```jac
|
|
8
|
+
"""AI agent with LLM-driven routing between handler nodes."""
|
|
9
|
+
|
|
10
|
+
import from byllm.lib { Model }
|
|
11
|
+
|
|
12
|
+
glob llm: Model = Model(model_name="gpt-4.1-mini");
|
|
13
|
+
|
|
14
|
+
node Router {}
|
|
15
|
+
|
|
16
|
+
node QAHandler {
|
|
17
|
+
def respond(message: str, context: list[dict]) -> str by llm(
|
|
18
|
+
method="Reason",
|
|
19
|
+
messages=context,
|
|
20
|
+
temperature=0.3
|
|
21
|
+
);
|
|
22
|
+
can handle with process_request entry;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
node CodeHandler {
|
|
26
|
+
def respond(message: str, context: list[dict]) -> str by llm(
|
|
27
|
+
messages=context,
|
|
28
|
+
tools=[search_docs]
|
|
29
|
+
);
|
|
30
|
+
can handle with process_request entry;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
walker :pub process_request {
|
|
34
|
+
has message: str = "";
|
|
35
|
+
has context: list[dict] = [];
|
|
36
|
+
|
|
37
|
+
can route with Router entry {
|
|
38
|
+
visit [-->] by llm(
|
|
39
|
+
incl_info={"message": self.message, "context": self.context[-5:]}
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
can init_graph with Root entry {
|
|
44
|
+
visit [-->][?:Router] else {
|
|
45
|
+
router = (here ++> Router())[0];
|
|
46
|
+
router ++> QAHandler();
|
|
47
|
+
router ++> CodeHandler();
|
|
48
|
+
visit router;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Implementation: impl/agent.impl.jac
|
|
55
|
+
|
|
56
|
+
```jac
|
|
57
|
+
sem Router.classify = """Return exactly one label: QA, CODE, or DONE.
|
|
58
|
+
QA = general questions, CODE = programming tasks, DONE = task complete.""";
|
|
59
|
+
|
|
60
|
+
sem QAHandler.respond = """You are a helpful assistant. Answer clearly and concisely.""";
|
|
61
|
+
|
|
62
|
+
sem CodeHandler.respond = """You are an expert programmer.
|
|
63
|
+
Use search_docs to find documentation before answering.""";
|
|
64
|
+
|
|
65
|
+
impl QAHandler.handle with process_request entry {
|
|
66
|
+
response = here.respond(message=self.message, context=self.context);
|
|
67
|
+
report {"response": response, "handler": "qa"};
|
|
68
|
+
visit [<--][?:Router];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
impl CodeHandler.handle with process_request entry {
|
|
72
|
+
response = here.respond(message=self.message, context=self.context);
|
|
73
|
+
report {"response": response, "handler": "code"};
|
|
74
|
+
visit [<--][?:Router];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
impl process_request.route with Router entry {
|
|
78
|
+
label = here.classify(message=self.message, context=self.context);
|
|
79
|
+
if "DONE" in label { disengage; }
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Key Patterns Demonstrated
|
|
84
|
+
|
|
85
|
+
- **Lazy graph creation**: `visit [-->][?:Router] else { ... }` creates graph on first use
|
|
86
|
+
- **LLM-driven routing**: `visit [-->] by llm(...)` lets LLM choose which handler
|
|
87
|
+
- **Interface/impl split**: Declarations in `.jac`, semantics + logic in `.impl.jac`
|
|
88
|
+
- **Multi-node traversal**: Walker visits Router → Handler → back to Router
|
|
89
|
+
- **`sem` annotations**: System prompts for each LLM-powered method
|
|
90
|
+
- **`disengage`**: Stops walker when task is complete
|