-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
155 lines (131 loc) · 5.2 KB
/
main.py
File metadata and controls
155 lines (131 loc) · 5.2 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
import os
import argparse
from dotenv import load_dotenv
from google import genai
from google.genai import types
from functions.get_files_info import schema_get_files_info, get_files_info
from functions.get_file_content import schema_get_file_content, get_file_content
from functions.write_file import schema_write_file, write_file
from functions.run_python_file import schema_run_python_file, run_python_file
def call_function(
function_call_part: types.FunctionCall, verbose=False
) -> types.Content:
if verbose:
print(f"Calling function: {function_call_part.name}({function_call_part.args})")
else:
print(f" - Calling function: {function_call_part.name}")
lut = {
"get_file_content": get_file_content,
"get_files_info": get_files_info,
"write_file": write_file,
"run_python_file": run_python_file,
}
function_name = function_call_part.name
assert function_name is not None
if function_call_part.name not in lut:
return types.Content(
role="tool",
parts=[
types.Part.from_function_response(
name=function_name,
response={"error": f"Unknown function: {function_name}"},
)
],
)
if function_call_part.args is None:
return types.Content(
role="tool",
parts=[
types.Part.from_function_response(
name=function_name,
response={"error": f"Missing args for: {function_name}"},
)
],
)
result = lut[function_name](
working_directory="./calculator/", **(function_call_part.args)
)
return types.Content(
role="tool",
parts=[
types.Part.from_function_response(
name=function_name,
response={"result": result},
)
],
)
def generate_content(client, messages, content_config, verbose):
response = client.models.generate_content(
model="gemini-2.0-flash-001", contents=messages, config=content_config
)
function_results = []
if response.candidates:
for candidate in response.candidates:
content = candidate.content
if content:
messages.append(
types.Content(role=content.role, parts=content.parts)
)
if response.usage_metadata is None:
raise Exception("Did not get response usage meta data.")
if response.function_calls:
for function_call_part in response.function_calls:
result = call_function(function_call_part, verbose)
if not result.parts or not result.parts[0].function_response:
raise Exception(
f"Error: expected a result from the function {function_call_part.name}"
)
function_results.append(result.parts[0])
if verbose:
print(f"-> {result.parts[0].function_response.response}")
messages.append(types.Content(role="user", parts=function_results))
if verbose:
print(f"User prompt: {content_config.system_instruction}")
print(f"Prompt tokens: {response.usage_metadata.prompt_token_count}")
print(f"Response tokens: {response.usage_metadata.candidates_token_count}")
if not response.function_calls:
print(f"Final response:\n{response.text}")
return True
return False
def main():
parser = argparse.ArgumentParser(prog="agent", description="My own custom agent.")
parser.add_argument("prompt")
parser.add_argument("-v", "--verbose", action="store_true")
args = parser.parse_args()
user_prompt = args.prompt
verbose = args.verbose
messages = [
types.Content(role="user", parts=[types.Part(text=user_prompt)]),
]
system_prompt = """
You are a helpful AI coding agent.
When a user asks a question or makes a request, make a function call plan. You can perform the following operations:
- List files and directories. Don't ask for confirmation if this is the function you think you need to execute.
- Read file contents. Don't ask for confirmation if this is the function you think you need to execute.
- Execute Python files with optional arguments
- Write or overwrite files
All paths you provide should be relative to the working directory. You do not need to specify the working directory in your function calls as it is automatically injected for security reasons.
"""
available_functions = types.Tool(
function_declarations=[
schema_get_files_info,
schema_get_file_content,
schema_write_file,
schema_run_python_file,
]
)
load_dotenv()
api_key = os.environ.get("GEMINI_API_KEY")
client = genai.Client(api_key=api_key)
content_config = types.GenerateContentConfig(
tools=[available_functions], system_instruction=system_prompt
)
for _ in range(20):
try:
done = generate_content(client, messages, content_config, verbose)
if done:
break
except Exception as e:
print(f"Feedback loop iteration got exception: {e}")
if __name__ == "__main__":
main()