Skip to content

Commit 181c11e

Browse files
committed
Initial commit of a new tutorial.
0 parents  commit 181c11e

12 files changed

Lines changed: 323 additions & 0 deletions

‎.gitignore‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.env
2+
.venv
3+
docs
4+
.idea
5+
.vscode
6+
.DS_Store

‎fizzbuzz.go‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package main
2+
3+
import "fmt"
4+
5+
func fizzbuzz(n int) {
6+
for i := 1; i <= n; i++ {
7+
switch {
8+
case i%15 == 0:
9+
fmt.Println("FizzBuzz")
10+
case i%3 == 0:
11+
fmt.Println("Fizz")
12+
case i%5 == 0:
13+
fmt.Println("Buzz")
14+
default:
15+
fmt.Println(i)
16+
}
17+
}
18+
}
19+
20+
func main() {
21+
// Execute FizzBuzz for numbers 1 to 15
22+
fizzbuzz(15)
23+
}

‎fizzbuzz.js‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// FizzBuzz implementation in Node.js
2+
function fizzbuzz(n) {
3+
for (let i = 1; i <= n; i++) {
4+
if (i % 15 === 0) {
5+
console.log('FizzBuzz');
6+
} else if (i % 3 === 0) {
7+
console.log('Fizz');
8+
} else if (i % 5 === 0) {
9+
console.log('Buzz');
10+
} else {
11+
console.log(i);
12+
}
13+
}
14+
}
15+
16+
// Execute FizzBuzz for numbers 1 to 15
17+
fizzbuzz(15);

‎main.py‎

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import os
2+
import sys
3+
from openai import OpenAI
4+
from dotenv import load_dotenv
5+
import json
6+
7+
from utils.console import Console
8+
from tools import ToolDefinition, read_file_tool, list_files_tool, edit_file_tool
9+
10+
load_dotenv()
11+
12+
api_key = os.environ.get("DEEPSEEK_API_KEY")
13+
if not api_key:
14+
Console.error("DEEPSEEK_API_KEY not found in .env")
15+
sys.exit(1)
16+
17+
client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com/")
18+
MODEL = "deepseek-chat"
19+
20+
class Agent:
21+
def __init__(self, client_instance, tool_definitions: list[ToolDefinition] = []):
22+
self.client = client_instance
23+
self.tool_definitions = {tool.name: tool for tool in tool_definitions} # Convert to dict for faster lookup
24+
25+
def execute_tool_call(self, tool_call):
26+
try:
27+
tool_name = tool_call.function.name
28+
tool_args_str = tool_call.function.arguments
29+
30+
if tool_name not in self.tool_definitions:
31+
return f"Error: Tool '{tool_name}' not found"
32+
tool_args = json.loads(tool_args_str)
33+
result = self.tool_definitions[tool_name].function(**tool_args)
34+
return result
35+
except Exception as e:
36+
return f"Error processing tool call: {str(e)}"
37+
38+
def run_inference(self, conversation_history):
39+
try:
40+
response = self.client.chat.completions.create(
41+
model=MODEL,
42+
messages=conversation_history,
43+
max_tokens=1024,
44+
tools=[tool.get_api_description() for tool in self.tool_definitions.values()],
45+
)
46+
return response.choices[0].message
47+
except Exception as e:
48+
Console.error(f"API Error: {e}")
49+
return None
50+
51+
def run(self):
52+
conversation = []
53+
Console.info(f"Chatting with {MODEL}. Hit Ctrl+C to exit.")
54+
while True:
55+
try:
56+
user_input = Console.user("")
57+
if not user_input:
58+
continue
59+
60+
conversation.append({"role": "user", "content": user_input})
61+
62+
while True:
63+
assistant_message_obj = self.run_inference(conversation)
64+
65+
if not assistant_message_obj:
66+
if conversation and conversation[-1]["role"] == "user":
67+
conversation.pop()
68+
break
69+
70+
assistant_message = assistant_message_obj.model_dump(exclude_unset=True)
71+
conversation.append(assistant_message)
72+
73+
if assistant_message_obj.content:
74+
Console.assistant(MODEL, assistant_message_obj.content)
75+
76+
if assistant_message_obj.tool_calls:
77+
tool_call_responses = []
78+
for tool_call in assistant_message_obj.tool_calls:
79+
Console.tool_call(tool_call.function.name, tool_call.function.arguments)
80+
tool_result = self.execute_tool_call(tool_call)
81+
tool_response = {
82+
"role": "tool",
83+
"tool_call_id": tool_call.id,
84+
"name": tool_call.function.name,
85+
"content": str(tool_result)
86+
}
87+
conversation.append(tool_response)
88+
tool_call_responses.append(tool_response)
89+
continue
90+
break
91+
92+
except KeyboardInterrupt:
93+
Console.info("\nExiting...")
94+
break
95+
96+
Console.info("Agent run() finished.")
97+
98+
99+
if __name__ == "__main__":
100+
agent_instance = Agent(client_instance=client, tool_definitions=[read_file_tool, list_files_tool, edit_file_tool])
101+
agent_instance.run()

‎tools/__init__.py‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from .tool_definition import ToolDefinition
2+
from .file_operations import read_file_from_path, list_files_from_path, edit_file
3+
4+
read_file_tool = ToolDefinition(
5+
name="read_file",
6+
description="Read the contents of a file",
7+
parameters={
8+
"type": "object",
9+
"properties": {
10+
"file_path": {
11+
"type": "string",
12+
"description": "Path to the file to read"
13+
}
14+
},
15+
"required": ["file_path"]
16+
},
17+
function_to_call=read_file_from_path
18+
)
19+
20+
list_files_tool = ToolDefinition(
21+
name="list_files",
22+
description="List files and directories at a given path. If no path is provided, lists files in the current directory.",
23+
parameters={
24+
"type": "object",
25+
"properties": {
26+
"path": {
27+
"type": "string",
28+
"description": "Optional relative path to list files from. Defaults to current directory if not provided."
29+
}
30+
}
31+
},
32+
function_to_call=list_files_from_path
33+
)
34+
35+
edit_file_tool = ToolDefinition(
36+
name="edit_file",
37+
description="Make edits to a text file. Replaces 'old_str' with 'new_str' in the given file. 'old_str' and 'new_str' MUST be different from each other. If the file specified with path doesn't exist, it will be created.",
38+
parameters={
39+
"type": "object",
40+
"properties": {
41+
"path": {
42+
"type": "string",
43+
"description": "The path to the file"
44+
},
45+
"old_str": {
46+
"type": "string",
47+
"description": "Text to search for - must match exactly and must only have one match exactly"
48+
},
49+
"new_str": {
50+
"type": "string",
51+
"description": "Text to replace old_str with"
52+
}
53+
},
54+
"required": ["path", "old_str", "new_str"]
55+
},
56+
function_to_call=edit_file
57+
)
1.62 KB
Binary file not shown.
4.66 KB
Binary file not shown.
1.14 KB
Binary file not shown.

‎tools/file_operations.py‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import os
2+
import json
3+
4+
5+
def read_file_from_path(file_path: str) -> str:
6+
try:
7+
with open(file_path, 'r', encoding='utf-8') as f:
8+
content = f.read()
9+
return content
10+
except FileNotFoundError:
11+
raise FileNotFoundError(f"Error: File not found at path: {file_path}")
12+
except Exception as e:
13+
raise Exception(f"An error occurred while reading the file '{file_path}': {e}")
14+
15+
16+
def list_files_from_path(path: str = ".") -> str:
17+
try:
18+
file_list = []
19+
for root, dirs, files in os.walk(path):
20+
if root == path:
21+
for d in dirs:
22+
file_list.append(f"{d}/")
23+
for f in files:
24+
file_list.append(f)
25+
else:
26+
rel_path = os.path.relpath(root, path)
27+
for d in dirs:
28+
file_list.append(f"{rel_path}/{d}/")
29+
for f in files:
30+
file_list.append(f"{rel_path}/{f}")
31+
return json.dumps(file_list)
32+
except Exception as e:
33+
raise Exception(f"An error occurred while listing files at path '{path}': {e}")
34+
35+
def create_new_file(file_path: str, content: str) -> str:
36+
dir_path = os.path.dirname(file_path)
37+
if dir_path and dir_path != ".":
38+
os.makedirs(dir_path, exist_ok=True)
39+
40+
with open(file_path, 'w', encoding='utf-8') as f:
41+
f.write(content)
42+
43+
return f"Successfully created file {file_path}"
44+
45+
46+
def edit_file(path: str, old_str: str, new_str: str) -> str:
47+
if path == "" or old_str == new_str:
48+
raise ValueError("Invalid input parameters")
49+
50+
try:
51+
try:
52+
with open(path, 'r', encoding='utf-8') as f:
53+
old_content = f.read()
54+
55+
if old_str == "":
56+
raise FileNotFoundError() # Treat empty old_str as file not found for creation logic
57+
58+
new_content = old_content.replace(old_str, new_str)
59+
60+
if old_content == new_content and old_str != "":
61+
raise ValueError("old_str not found in file")
62+
63+
with open(path, 'w', encoding='utf-8') as f:
64+
f.write(new_content)
65+
66+
return f"Successfully edited file {path}"
67+
68+
except FileNotFoundError:
69+
if old_str == "": # If old_str is empty and file not found, create new file
70+
return create_new_file(path, new_str)
71+
raise
72+
except Exception as e:
73+
raise Exception(f"An error occurred while editing the file '{path}': {e}")

‎tools/tool_definition.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import typing
2+
3+
class ToolDefinition:
4+
def __init__(self, name: str, description: str, parameters: dict, function_to_call: typing.Callable):
5+
self.name = name
6+
self.description = description
7+
self.parameters = parameters
8+
self.function = function_to_call
9+
10+
def get_api_description(self) -> dict:
11+
return {
12+
"type": "function",
13+
"function": {
14+
"name": self.name,
15+
"description": self.description,
16+
"parameters": self.parameters,
17+
}
18+
}

0 commit comments

Comments
 (0)