This repository was archived by the owner on Jan 14, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathcodeact_agent.py
More file actions
175 lines (128 loc) · 4.55 KB
/
codeact_agent.py
File metadata and controls
175 lines (128 loc) · 4.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# pip install langgraph-codeact "langchain[anthropic]"
import asyncio
import inspect
import uuid
from typing import Any
from langchain.chat_models import init_chat_model
from langchain_sandbox import PyodideSandbox
from langgraph_codeact import EvalCoroutine, create_codeact
def create_pyodide_eval_fn(sandbox: PyodideSandbox) -> EvalCoroutine:
"""Create an eval_fn that uses PyodideSandbox.
"""
async def async_eval_fn(
code: str, _locals: dict[str, Any]
) -> tuple[str, dict[str, Any]]:
# Create a wrapper function that will execute the code and return locals
wrapper_code = f"""
def execute():
try:
# Execute the provided code
{chr(10).join(" " + line for line in code.strip().split(chr(10)))}
return locals()
except Exception as e:
return {{"error": str(e)}}
execute()
"""
# Convert functions in _locals to their string representation
context_setup = ""
for key, value in _locals.items():
if callable(value):
# Get the function's source code
src = inspect.getsource(value)
context_setup += f"\n{src}"
else:
context_setup += f"\n{key} = {repr(value)}"
try:
# Execute the code and get the result
response = await sandbox.execute(
code=context_setup + "\n\n" + wrapper_code,
)
# Check if execution was successful
if response.stderr:
return f"Error during execution: {response.stderr}", {}
# Get the output from stdout
output = (
response.stdout
if response.stdout
else "<Code ran, no output printed to stdout>"
)
result = response.result
# If there was an error in the result, return it
if isinstance(result, dict) and "error" in result:
return f"Error during execution: {result['error']}", {}
# Get the new variables by comparing with original locals
new_vars = {
k: v
for k, v in result.items()
if k not in _locals and not k.startswith("_")
}
return output, new_vars
except Exception as e:
return f"Error during PyodideSandbox execution: {repr(e)}", {}
return async_eval_fn
def add(a: float, b: float) -> float:
"""Add two numbers together."""
return a + b
def multiply(a: float, b: float) -> float:
"""Multiply two numbers together."""
return a * b
def divide(a: float, b: float) -> float:
"""Divide two numbers."""
return a / b
def subtract(a: float, b: float) -> float:
"""Subtract two numbers."""
return a - b
def sin(a: float) -> float:
"""Take the sine of a number."""
import math
return math.sin(a)
def cos(a: float) -> float:
"""Take the cosine of a number."""
import math
return math.cos(a)
def radians(a: float) -> float:
"""Convert degrees to radians."""
import math
return math.radians(a)
def exponentiation(a: float, b: float) -> float:
"""Raise one number to the power of another."""
return a**b
def sqrt(a: float) -> float:
"""Take the square root of a number."""
import math
return math.sqrt(a)
def ceil(a: float) -> float:
"""Round a number up to the nearest integer."""
import math
return math.ceil(a)
tools = [
add,
multiply,
divide,
subtract,
sin,
cos,
radians,
exponentiation,
sqrt,
ceil,
]
model = init_chat_model("claude-3-7-sonnet-latest", model_provider="anthropic")
sandbox = PyodideSandbox(allow_net=True)
eval_fn = create_pyodide_eval_fn(sandbox)
code_act = create_codeact(model, tools, eval_fn)
agent = code_act.compile()
query = """A batter hits a baseball at 45.847 m/s at an angle of 23.474° above the horizontal. The outfielder, who starts facing the batter, picks up the baseball as it lands, then throws it back towards the batter at 24.12 m/s at an angle of 39.12 degrees. How far is the baseball from where the batter originally hit it? Assume zero air resistance."""
async def run_agent(query: str):
# Stream agent outputs
async for typ, chunk in agent.astream(
{"messages": query},
stream_mode=["values", "messages"],
):
if typ == "messages":
print(chunk[0].content, end="")
elif typ == "values":
print("\n\n---answer---\n\n", chunk)
if __name__ == "__main__":
# Run the agent
asyncio.run(run_agent(query))