closecode-ai 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.
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.4
2
+ Name: closecode-ai
3
+ Version: 0.1.0
4
+ Summary: CloseCode — an agentic terminal coding assistant (LangGraph + OpenRouter + MCP)
5
+ Author: Om Gite
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: langchain
10
+ Requires-Dist: langgraph
11
+ Requires-Dist: langchain-huggingface
12
+ Requires-Dist: langsmith
13
+ Requires-Dist: huggingface_hub
14
+ Requires-Dist: python-dotenv
15
+ Requires-Dist: requests
16
+ Requires-Dist: langchain-openai
17
+ Requires-Dist: langchain-mcp-adapters
18
+ Requires-Dist: mcp-server-git
19
+ Requires-Dist: langchain-openrouter
20
+ Requires-Dist: rich
21
+ Requires-Dist: pyfiglet
22
+
23
+ # terminal-agent
24
+
25
+ A minimal terminal coding agent built with LangGraph (agent loop), LangChain
26
+ (tool + model abstraction), LangSmith (tracing), and a Hugging Face model as
27
+ the LLM. Same shape as OpenCode/Terminus 2: read task -> decide -> run tool ->
28
+ observe result -> repeat.
29
+
30
+ ## Setup
31
+
32
+ ```bash
33
+ pip install -r requirements.txt
34
+ cp .env.example .env
35
+ ```
36
+
37
+ Edit `.env`:
38
+ - `HUGGINGFACEHUB_API_TOKEN` — from https://huggingface.co/settings/tokens
39
+ - `HF_MODEL_ID` — a model that supports tool/function calling (see note below)
40
+ - `LANGCHAIN_API_KEY` — optional, from https://smith.langchain.com, enables tracing
41
+
42
+ ## Run
43
+
44
+ ```bash
45
+ python main.py
46
+ ```
47
+
48
+ If `OPENROUTER_API_KEY` isn't set (env or `.env`), the agent prompts you to
49
+ paste one at startup — input is hidden — and offers to save it to `.env`
50
+ for next time. You can rotate it later with the `/key` command.
51
+
52
+ ## Switching models
53
+
54
+ ```text
55
+ /models list all OpenRouter models (● = current, free first)
56
+ /models qwen filter the list by name
57
+ /models --refresh force a fresh fetch (list is cached for 24h)
58
+ /model 12 switch by list number
59
+ /model qwen/qwen-2.5-72b-instruct or any OpenRouter model id directly
60
+ ```
61
+
62
+ The list comes live from OpenRouter's API and is cached for 24 hours in
63
+ `~/.cache/closecode/`; if you're offline it falls back to a curated
64
+ shortlist. The model choice is saved per session, so `/resume` restores
65
+ the model you were using. Free-tier `:free` models cost nothing; check
66
+ https://openrouter.ai/models for paid-model pricing before switching.
67
+
68
+ The agent operates inside `./sandbox` (configurable via `AGENT_WORKDIR`) and
69
+ will ask for permission before running shell commands or writing files,
70
+ unless `AGENT_AUTO_APPROVE=true`.
71
+
72
+ ## Persistent sessions (SQLite)
73
+
74
+ Conversations are stored in `./sessions/sessions.db` as per-message rows plus
75
+ session metadata (name, model, mode, created/updated timestamps, message
76
+ count). Existing `session_*.json` files are auto-migrated into the DB on the
77
+ first run.
78
+
79
+ - `--continue` — resume the most recently used session (its mode/model are
80
+ restored from metadata).
81
+ - `/sessions` — list all saved sessions with metadata.
82
+ - `/resume <id>` — switch to a saved conversation.
83
+ - `/delete <id>` — delete a saved session.
84
+ - Each turn is saved automatically; a session's name is derived from its
85
+ first user message.
86
+
87
+ ## Guardrails
88
+
89
+ The agent is scoped to coding only, and `guardrails.py` enforces that with
90
+ four layers:
91
+
92
+ 1. **Input scope** — off-topic chatter (greetings, opinions, trivia, creative
93
+ writing, news takes) is redirected to a coding task, and clearly malicious
94
+ requests (keyloggers, account hacking, phishing kits) are refused before
95
+ they reach the model.
96
+ 2. **Command blocking** — destructive shells commands (`rm -rf /`, `mkfs`,
97
+ disk wipes, fork bombs, `curl | sh`, reverse shells) are blocked before
98
+ execution, *even when auto-approve is on*.
99
+ 3. **Write scanning** — file writes/edits containing malware indicators
100
+ (ransomware, keyloggers, miners, persistence, injection) are refused.
101
+ 4. **Output redaction** — the model's final answer is scanned and flagged
102
+ content is scrubbed from conversation history.
103
+
104
+ These are conservative heuristics on top of the sandbox + per-action
105
+ permission prompts, not a hard guarantee. Set `AGENT_DISABLE_GUARDRAILS=true`
106
+ in `.env` to disable them entirely (only for trusted, isolated testing).
107
+
108
+ ## A note on model choice
109
+
110
+ Tool-calling reliability varies significantly across open Hugging Face
111
+ models — this is the single biggest factor in whether this agent actually
112
+ works well. Frontier closed models (what Claude Code / OpenCode use by
113
+ default) are heavily trained specifically for reliable tool use; open models
114
+ are improving but inconsistent.
115
+
116
+ Models worth trying, roughly in order of tool-calling reliability:
117
+ - `Qwen/Qwen2.5-72B-Instruct`
118
+ - `meta-llama/Meta-Llama-3.1-70B-Instruct`
119
+ - `meta-llama/Meta-Llama-3.1-8B-Instruct` (fastest/cheapest, least reliable)
120
+
121
+ If a smaller model frequently fails to call tools correctly, or hallucinates
122
+ tool arguments, that's expected — it's a real, documented gap between open
123
+ and closed models on agentic tasks, not a bug in this code. Swapping
124
+ `HF_MODEL_ID` is the first thing to try before changing anything else.
125
+
126
+ ## Where to go next
127
+
128
+ 1. **Watch a trace in LangSmith** (smith.langchain.com) once you have a run —
129
+ seeing the exact messages/tool calls at each step is the fastest way to
130
+ debug why the agent did something unexpected.
131
+ 2. **Add more tools** — `list_dir`, `edit_file` (targeted find/replace instead
132
+ of full overwrite), `run_tests`.
133
+ 3. **Split client/server** — move `build_graph()` behind a small FastAPI/
134
+ WebSocket server, and make `main.py` a thin client that streams from it.
135
+ This is the step that makes it architecturally closer to OpenCode.
136
+ 4. **Swap the sandbox for Docker** — the current harness restricts file paths
137
+ but shell commands still run on your actual machine. For anything beyond
138
+ personal experimentation, run `bash` calls inside a container instead.
@@ -0,0 +1,116 @@
1
+ # terminal-agent
2
+
3
+ A minimal terminal coding agent built with LangGraph (agent loop), LangChain
4
+ (tool + model abstraction), LangSmith (tracing), and a Hugging Face model as
5
+ the LLM. Same shape as OpenCode/Terminus 2: read task -> decide -> run tool ->
6
+ observe result -> repeat.
7
+
8
+ ## Setup
9
+
10
+ ```bash
11
+ pip install -r requirements.txt
12
+ cp .env.example .env
13
+ ```
14
+
15
+ Edit `.env`:
16
+ - `HUGGINGFACEHUB_API_TOKEN` — from https://huggingface.co/settings/tokens
17
+ - `HF_MODEL_ID` — a model that supports tool/function calling (see note below)
18
+ - `LANGCHAIN_API_KEY` — optional, from https://smith.langchain.com, enables tracing
19
+
20
+ ## Run
21
+
22
+ ```bash
23
+ python main.py
24
+ ```
25
+
26
+ If `OPENROUTER_API_KEY` isn't set (env or `.env`), the agent prompts you to
27
+ paste one at startup — input is hidden — and offers to save it to `.env`
28
+ for next time. You can rotate it later with the `/key` command.
29
+
30
+ ## Switching models
31
+
32
+ ```text
33
+ /models list all OpenRouter models (● = current, free first)
34
+ /models qwen filter the list by name
35
+ /models --refresh force a fresh fetch (list is cached for 24h)
36
+ /model 12 switch by list number
37
+ /model qwen/qwen-2.5-72b-instruct or any OpenRouter model id directly
38
+ ```
39
+
40
+ The list comes live from OpenRouter's API and is cached for 24 hours in
41
+ `~/.cache/closecode/`; if you're offline it falls back to a curated
42
+ shortlist. The model choice is saved per session, so `/resume` restores
43
+ the model you were using. Free-tier `:free` models cost nothing; check
44
+ https://openrouter.ai/models for paid-model pricing before switching.
45
+
46
+ The agent operates inside `./sandbox` (configurable via `AGENT_WORKDIR`) and
47
+ will ask for permission before running shell commands or writing files,
48
+ unless `AGENT_AUTO_APPROVE=true`.
49
+
50
+ ## Persistent sessions (SQLite)
51
+
52
+ Conversations are stored in `./sessions/sessions.db` as per-message rows plus
53
+ session metadata (name, model, mode, created/updated timestamps, message
54
+ count). Existing `session_*.json` files are auto-migrated into the DB on the
55
+ first run.
56
+
57
+ - `--continue` — resume the most recently used session (its mode/model are
58
+ restored from metadata).
59
+ - `/sessions` — list all saved sessions with metadata.
60
+ - `/resume <id>` — switch to a saved conversation.
61
+ - `/delete <id>` — delete a saved session.
62
+ - Each turn is saved automatically; a session's name is derived from its
63
+ first user message.
64
+
65
+ ## Guardrails
66
+
67
+ The agent is scoped to coding only, and `guardrails.py` enforces that with
68
+ four layers:
69
+
70
+ 1. **Input scope** — off-topic chatter (greetings, opinions, trivia, creative
71
+ writing, news takes) is redirected to a coding task, and clearly malicious
72
+ requests (keyloggers, account hacking, phishing kits) are refused before
73
+ they reach the model.
74
+ 2. **Command blocking** — destructive shells commands (`rm -rf /`, `mkfs`,
75
+ disk wipes, fork bombs, `curl | sh`, reverse shells) are blocked before
76
+ execution, *even when auto-approve is on*.
77
+ 3. **Write scanning** — file writes/edits containing malware indicators
78
+ (ransomware, keyloggers, miners, persistence, injection) are refused.
79
+ 4. **Output redaction** — the model's final answer is scanned and flagged
80
+ content is scrubbed from conversation history.
81
+
82
+ These are conservative heuristics on top of the sandbox + per-action
83
+ permission prompts, not a hard guarantee. Set `AGENT_DISABLE_GUARDRAILS=true`
84
+ in `.env` to disable them entirely (only for trusted, isolated testing).
85
+
86
+ ## A note on model choice
87
+
88
+ Tool-calling reliability varies significantly across open Hugging Face
89
+ models — this is the single biggest factor in whether this agent actually
90
+ works well. Frontier closed models (what Claude Code / OpenCode use by
91
+ default) are heavily trained specifically for reliable tool use; open models
92
+ are improving but inconsistent.
93
+
94
+ Models worth trying, roughly in order of tool-calling reliability:
95
+ - `Qwen/Qwen2.5-72B-Instruct`
96
+ - `meta-llama/Meta-Llama-3.1-70B-Instruct`
97
+ - `meta-llama/Meta-Llama-3.1-8B-Instruct` (fastest/cheapest, least reliable)
98
+
99
+ If a smaller model frequently fails to call tools correctly, or hallucinates
100
+ tool arguments, that's expected — it's a real, documented gap between open
101
+ and closed models on agentic tasks, not a bug in this code. Swapping
102
+ `HF_MODEL_ID` is the first thing to try before changing anything else.
103
+
104
+ ## Where to go next
105
+
106
+ 1. **Watch a trace in LangSmith** (smith.langchain.com) once you have a run —
107
+ seeing the exact messages/tool calls at each step is the fastest way to
108
+ debug why the agent did something unexpected.
109
+ 2. **Add more tools** — `list_dir`, `edit_file` (targeted find/replace instead
110
+ of full overwrite), `run_tests`.
111
+ 3. **Split client/server** — move `build_graph()` behind a small FastAPI/
112
+ WebSocket server, and make `main.py` a thin client that streams from it.
113
+ This is the step that makes it architecturally closer to OpenCode.
114
+ 4. **Swap the sandbox for Docker** — the current harness restricts file paths
115
+ but shell commands still run on your actual machine. For anything beyond
116
+ personal experimentation, run `bash` calls inside a container instead.
@@ -0,0 +1,74 @@
1
+
2
+ from typing import Annotated, TypedDict
3
+
4
+ from langgraph.graph import StateGraph
5
+ from langgraph.graph.message import add_messages
6
+ from langgraph.prebuilt import ToolNode, tools_condition
7
+
8
+ from llm import get_llm
9
+
10
+ SYSTEM_PROMPT = """You are a terminal coding agent running in a sandboxed working directory.
11
+ You have tools for reading, writing, and editing files, running shell commands and tests,
12
+ listing directories, and interacting with git (status, diff, log, commit, branches).
13
+
14
+ Note: your available tools change depending on the current mode. In "plan" mode only
15
+ read-only tools are bound to you (you literally cannot call write/edit/bash/commit tools
16
+ even if you wanted to) — use that mode to explore and propose an approach without any
17
+ risk of side effects. In "build" mode all tools are available.
18
+
19
+ Rules:
20
+ - Inspect before you change: look at relevant files or run a command to understand
21
+ the current state before editing anything.
22
+ - Prefer edit_file over write_file for small changes — it's cheaper and safer than
23
+ rewriting a whole file.
24
+ - Call one tool at a time and read its result before deciding the next step.
25
+ - Verify your work: after making a change, run a command, run tests, or read the
26
+ file back to confirm it did what you intended. Never report a task complete
27
+ without verifying — bugs are unacceptable, so test before you say "done".
28
+ - Use git tools deliberately: check status/diff before committing, and never force-push
29
+ or hard-reset unless the user explicitly asked for that specific action.
30
+ - When the task is complete, reply with plain text summarizing what you did.
31
+ Do not call a tool in the same turn as your final summary.
32
+
33
+ Scope & safety:
34
+ - You are a coding assistant, not a general chat assistant. If the user asks for
35
+ something unrelated to code (greetings, small-talk, opinions, news, games,
36
+ creative writing, trivia), politely redirect them back to a concrete coding task
37
+ instead of entertaining it.
38
+ - Never write working malware, ransomware, keyloggers, reverse shells, credential
39
+ stealers, or exploit payloads, and never otherwise follow a request to create
40
+ malicious software. If asked, refuse and suggest a safe, defensive framing.
41
+ - Mechanical guardrails additionally enforce this: destructive shell commands are
42
+ blocked before execution, file writes are scanned for malware indicators, and
43
+ flagged output is scrubbed from history. If a tool returns a "Guardrail blocked"
44
+ message, that means your proposed action was refused by policy — choose a safer
45
+ alternative and explain the refusal to the user.
46
+ """
47
+
48
+
49
+ class AgentState(TypedDict):
50
+ messages: Annotated[list, add_messages]
51
+
52
+
53
+ def build_graph(tools: list, model_override: str = None):
54
+ """tools: the combined list of local tools (bash, read_file, etc.) and
55
+ any MCP-provided tools (e.g. git) — assembled by main.py before this
56
+ is called, since loading MCP tools is async.
57
+
58
+ model_override: pass a model ID to use instead of whatever llm.py
59
+ defaults to. Requires get_llm() in your llm.py to accept an optional
60
+ override argument — see the note in main.py's /model command if it
61
+ doesn't yet."""
62
+ llm_with_tools = get_llm(model_override).bind_tools(tools)
63
+
64
+ def call_model(state: AgentState):
65
+ response = llm_with_tools.invoke(state["messages"])
66
+ return {"messages": [response]}
67
+
68
+ graph = StateGraph(AgentState)
69
+ graph.add_node("agent", call_model)
70
+ graph.add_node("tools", ToolNode(tools))
71
+ graph.set_entry_point("agent")
72
+ graph.add_conditional_edges("agent", tools_condition)
73
+ graph.add_edge("tools", "agent")
74
+ return graph.compile()
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.4
2
+ Name: closecode-ai
3
+ Version: 0.1.0
4
+ Summary: CloseCode — an agentic terminal coding assistant (LangGraph + OpenRouter + MCP)
5
+ Author: Om Gite
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: langchain
10
+ Requires-Dist: langgraph
11
+ Requires-Dist: langchain-huggingface
12
+ Requires-Dist: langsmith
13
+ Requires-Dist: huggingface_hub
14
+ Requires-Dist: python-dotenv
15
+ Requires-Dist: requests
16
+ Requires-Dist: langchain-openai
17
+ Requires-Dist: langchain-mcp-adapters
18
+ Requires-Dist: mcp-server-git
19
+ Requires-Dist: langchain-openrouter
20
+ Requires-Dist: rich
21
+ Requires-Dist: pyfiglet
22
+
23
+ # terminal-agent
24
+
25
+ A minimal terminal coding agent built with LangGraph (agent loop), LangChain
26
+ (tool + model abstraction), LangSmith (tracing), and a Hugging Face model as
27
+ the LLM. Same shape as OpenCode/Terminus 2: read task -> decide -> run tool ->
28
+ observe result -> repeat.
29
+
30
+ ## Setup
31
+
32
+ ```bash
33
+ pip install -r requirements.txt
34
+ cp .env.example .env
35
+ ```
36
+
37
+ Edit `.env`:
38
+ - `HUGGINGFACEHUB_API_TOKEN` — from https://huggingface.co/settings/tokens
39
+ - `HF_MODEL_ID` — a model that supports tool/function calling (see note below)
40
+ - `LANGCHAIN_API_KEY` — optional, from https://smith.langchain.com, enables tracing
41
+
42
+ ## Run
43
+
44
+ ```bash
45
+ python main.py
46
+ ```
47
+
48
+ If `OPENROUTER_API_KEY` isn't set (env or `.env`), the agent prompts you to
49
+ paste one at startup — input is hidden — and offers to save it to `.env`
50
+ for next time. You can rotate it later with the `/key` command.
51
+
52
+ ## Switching models
53
+
54
+ ```text
55
+ /models list all OpenRouter models (● = current, free first)
56
+ /models qwen filter the list by name
57
+ /models --refresh force a fresh fetch (list is cached for 24h)
58
+ /model 12 switch by list number
59
+ /model qwen/qwen-2.5-72b-instruct or any OpenRouter model id directly
60
+ ```
61
+
62
+ The list comes live from OpenRouter's API and is cached for 24 hours in
63
+ `~/.cache/closecode/`; if you're offline it falls back to a curated
64
+ shortlist. The model choice is saved per session, so `/resume` restores
65
+ the model you were using. Free-tier `:free` models cost nothing; check
66
+ https://openrouter.ai/models for paid-model pricing before switching.
67
+
68
+ The agent operates inside `./sandbox` (configurable via `AGENT_WORKDIR`) and
69
+ will ask for permission before running shell commands or writing files,
70
+ unless `AGENT_AUTO_APPROVE=true`.
71
+
72
+ ## Persistent sessions (SQLite)
73
+
74
+ Conversations are stored in `./sessions/sessions.db` as per-message rows plus
75
+ session metadata (name, model, mode, created/updated timestamps, message
76
+ count). Existing `session_*.json` files are auto-migrated into the DB on the
77
+ first run.
78
+
79
+ - `--continue` — resume the most recently used session (its mode/model are
80
+ restored from metadata).
81
+ - `/sessions` — list all saved sessions with metadata.
82
+ - `/resume <id>` — switch to a saved conversation.
83
+ - `/delete <id>` — delete a saved session.
84
+ - Each turn is saved automatically; a session's name is derived from its
85
+ first user message.
86
+
87
+ ## Guardrails
88
+
89
+ The agent is scoped to coding only, and `guardrails.py` enforces that with
90
+ four layers:
91
+
92
+ 1. **Input scope** — off-topic chatter (greetings, opinions, trivia, creative
93
+ writing, news takes) is redirected to a coding task, and clearly malicious
94
+ requests (keyloggers, account hacking, phishing kits) are refused before
95
+ they reach the model.
96
+ 2. **Command blocking** — destructive shells commands (`rm -rf /`, `mkfs`,
97
+ disk wipes, fork bombs, `curl | sh`, reverse shells) are blocked before
98
+ execution, *even when auto-approve is on*.
99
+ 3. **Write scanning** — file writes/edits containing malware indicators
100
+ (ransomware, keyloggers, miners, persistence, injection) are refused.
101
+ 4. **Output redaction** — the model's final answer is scanned and flagged
102
+ content is scrubbed from conversation history.
103
+
104
+ These are conservative heuristics on top of the sandbox + per-action
105
+ permission prompts, not a hard guarantee. Set `AGENT_DISABLE_GUARDRAILS=true`
106
+ in `.env` to disable them entirely (only for trusted, isolated testing).
107
+
108
+ ## A note on model choice
109
+
110
+ Tool-calling reliability varies significantly across open Hugging Face
111
+ models — this is the single biggest factor in whether this agent actually
112
+ works well. Frontier closed models (what Claude Code / OpenCode use by
113
+ default) are heavily trained specifically for reliable tool use; open models
114
+ are improving but inconsistent.
115
+
116
+ Models worth trying, roughly in order of tool-calling reliability:
117
+ - `Qwen/Qwen2.5-72B-Instruct`
118
+ - `meta-llama/Meta-Llama-3.1-70B-Instruct`
119
+ - `meta-llama/Meta-Llama-3.1-8B-Instruct` (fastest/cheapest, least reliable)
120
+
121
+ If a smaller model frequently fails to call tools correctly, or hallucinates
122
+ tool arguments, that's expected — it's a real, documented gap between open
123
+ and closed models on agentic tasks, not a bug in this code. Swapping
124
+ `HF_MODEL_ID` is the first thing to try before changing anything else.
125
+
126
+ ## Where to go next
127
+
128
+ 1. **Watch a trace in LangSmith** (smith.langchain.com) once you have a run —
129
+ seeing the exact messages/tool calls at each step is the fastest way to
130
+ debug why the agent did something unexpected.
131
+ 2. **Add more tools** — `list_dir`, `edit_file` (targeted find/replace instead
132
+ of full overwrite), `run_tests`.
133
+ 3. **Split client/server** — move `build_graph()` behind a small FastAPI/
134
+ WebSocket server, and make `main.py` a thin client that streams from it.
135
+ This is the step that makes it architecturally closer to OpenCode.
136
+ 4. **Swap the sandbox for Docker** — the current harness restricts file paths
137
+ but shell commands still run on your actual machine. For anything beyond
138
+ personal experimentation, run `bash` calls inside a container instead.
@@ -0,0 +1,22 @@
1
+ README.md
2
+ agent.py
3
+ debug_response.py
4
+ guardrails.py
5
+ harness.py
6
+ llm.py
7
+ main.py
8
+ mcp_tools.py
9
+ modes.py
10
+ pyproject.toml
11
+ search.py
12
+ session.py
13
+ todos.py
14
+ token_tracker.py
15
+ tools.py
16
+ ui.py
17
+ closecode_ai.egg-info/PKG-INFO
18
+ closecode_ai.egg-info/SOURCES.txt
19
+ closecode_ai.egg-info/dependency_links.txt
20
+ closecode_ai.egg-info/entry_points.txt
21
+ closecode_ai.egg-info/requires.txt
22
+ closecode_ai.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ closecode = main:cli_entry
@@ -0,0 +1,13 @@
1
+ langchain
2
+ langgraph
3
+ langchain-huggingface
4
+ langsmith
5
+ huggingface_hub
6
+ python-dotenv
7
+ requests
8
+ langchain-openai
9
+ langchain-mcp-adapters
10
+ mcp-server-git
11
+ langchain-openrouter
12
+ rich
13
+ pyfiglet
@@ -0,0 +1,14 @@
1
+ agent
2
+ debug_response
3
+ guardrails
4
+ harness
5
+ llm
6
+ main
7
+ mcp_tools
8
+ modes
9
+ search
10
+ session
11
+ todos
12
+ token_tracker
13
+ tools
14
+ ui
@@ -0,0 +1,25 @@
1
+
2
+ import os
3
+ from dotenv import load_dotenv
4
+ load_dotenv()
5
+
6
+ from llm import get_llm
7
+ from tools import LOCAL_TOOLS
8
+ from langchain_core.messages import HumanMessage, SystemMessage
9
+ from agent import SYSTEM_PROMPT
10
+
11
+ llm = get_llm().bind_tools(LOCAL_TOOLS)
12
+ messages = [
13
+ SystemMessage(content=SYSTEM_PROMPT),
14
+ HumanMessage(content='add a third line to output.txt that says "done"'),
15
+ ]
16
+ response = llm.invoke(messages)
17
+
18
+ print("=== content ===")
19
+ print(repr(response.content))
20
+ print("\n=== tool_calls ===")
21
+ print(response.tool_calls)
22
+ print("\n=== additional_kwargs ===")
23
+ print(response.additional_kwargs)
24
+ print("\n=== response_metadata ===")
25
+ print(response.response_metadata)