Merge branch 'Fosowl:main' into main
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
# Python cache files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
venv/
|
||||||
|
.venv/
|
||||||
|
|
||||||
|
# Environment variables (secrets)
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Git metadata
|
||||||
|
.git/
|
||||||
|
|
||||||
|
# macOS Finder files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
*.log
|
||||||
@@ -9,6 +9,7 @@ test_agent.py
|
|||||||
config.ini
|
config.ini
|
||||||
.voices/
|
.voices/
|
||||||
experimental/
|
experimental/
|
||||||
|
chrome_bundle/
|
||||||
.logs/
|
.logs/
|
||||||
.screenshots/*.png
|
.screenshots/*.png
|
||||||
.screenshots/*.jpg
|
.screenshots/*.jpg
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
|
|||||||
|
|
||||||
Disclaimer: This demo, including all the files that appear (e.g: CV_candidates.zip), are entirely fictional. We are not a corporation, we seek open-source contributors not candidates.
|
Disclaimer: This demo, including all the files that appear (e.g: CV_candidates.zip), are entirely fictional. We are not a corporation, we seek open-source contributors not candidates.
|
||||||
|
|
||||||
> 🛠️ **Work in Progress** – Looking for contributors!
|
> 🛠⚠️️ **Active Work in Progress** – Please note that Code/Bash is not dockerized yet but will be soon (see docker_deployement branch) - Do not deploy over network or production.
|
||||||
|
|
||||||
|
> 🙏 This project started as a side-project with zero roadmap and zero funding. It's grown way beyond what I expected by ending in GitHub Trending. Contributions, feedback, and patience are deeply appreciated.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -552,8 +554,8 @@ We’re looking for developers to improve AgenticSeek! Check out open issues or
|
|||||||
|
|
||||||
## Maintainers:
|
## Maintainers:
|
||||||
|
|
||||||
> [Fosowl](https://github.com/Fosowl) | Paris Time | (Sometime busy)
|
> [Fosowl](https://github.com/Fosowl) | Paris Time
|
||||||
|
|
||||||
> [https://github.com/antoineVIVIES](antoineVIVIES) | Taipei Time | (Often busy)
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
||||||
|
|
||||||
> [steveh8758](https://github.com/steveh8758) | Taipei Time | (Always busy)
|
> [steveh8758](https://github.com/steveh8758) | Taipei Time
|
||||||
|
|||||||
+3
-3
@@ -564,8 +564,8 @@ DeepSeek R1 天生会说中文
|
|||||||
|
|
||||||
## 维护者:
|
## 维护者:
|
||||||
|
|
||||||
> [Fosowl](https://github.com/Fosowl) | 巴黎时间 | (有时很忙)
|
> [Fosowl](https://github.com/Fosowl) | 巴黎时间
|
||||||
|
|
||||||
> [https://github.com/antoineVIVIES](https://github.com/antoineVIVIES) | 台北时间 | (经常很忙)
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
||||||
|
|
||||||
> [steveh8758](https://github.com/steveh8758) | 台北时间 | (总是很忙)
|
> [steveh8758](https://github.com/steveh8758) | 台北时间
|
||||||
|
|||||||
+1
-1
@@ -495,4 +495,4 @@ Nous recherchons des développeurs pour améliorer AgenticSeek ! Consultez la se
|
|||||||
## Mainteneurs:
|
## Mainteneurs:
|
||||||
> [Fosowl](https://github.com/Fosowl)
|
> [Fosowl](https://github.com/Fosowl)
|
||||||
> [steveh8758](https://github.com/steveh8758)
|
> [steveh8758](https://github.com/steveh8758)
|
||||||
> [https://github.com/antoineVIVIES](https://github.com/antoineVIVIES)
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
||||||
|
|||||||
+1
-1
@@ -565,6 +565,6 @@ AgenticSeekを改善するための開発者を募集しています!オープ
|
|||||||
|
|
||||||
> [Fosowl](https://github.com/Fosowl) | パリ時間
|
> [Fosowl](https://github.com/Fosowl) | パリ時間
|
||||||
|
|
||||||
> [https://github.com/antoineVIVIES](antoineVIVIES) | 台北時間
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
||||||
|
|
||||||
> [steveh8758](https://github.com/steveh8758) | 台北時間 |(常に忙しい)
|
> [steveh8758](https://github.com/steveh8758) | 台北時間 |(常に忙しい)
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -140,8 +140,10 @@ class Agent():
|
|||||||
Remove the reasoning block of reasoning model like deepseek.
|
Remove the reasoning block of reasoning model like deepseek.
|
||||||
"""
|
"""
|
||||||
end_tag = "</think>"
|
end_tag = "</think>"
|
||||||
end_idx = text.rfind(end_tag)+8
|
end_idx = text.rfind(end_tag)
|
||||||
return text[end_idx:]
|
if end_idx == -1:
|
||||||
|
return text
|
||||||
|
return text[end_idx+8:]
|
||||||
|
|
||||||
def extract_reasoning_text(self, text: str) -> None:
|
def extract_reasoning_text(self, text: str) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
+32
-33
@@ -14,7 +14,6 @@ from openai import OpenAI
|
|||||||
from sources.logger import Logger
|
from sources.logger import Logger
|
||||||
from sources.utility import pretty_print, animate_thinking
|
from sources.utility import pretty_print, animate_thinking
|
||||||
|
|
||||||
|
|
||||||
class Provider:
|
class Provider:
|
||||||
def __init__(self, provider_name, model, server_address="127.0.0.1:5000", is_local=False):
|
def __init__(self, provider_name, model, server_address="127.0.0.1:5000", is_local=False):
|
||||||
self.provider_name = provider_name.lower()
|
self.provider_name = provider_name.lower()
|
||||||
@@ -58,38 +57,6 @@ class Provider:
|
|||||||
exit(1)
|
exit(1)
|
||||||
return api_key
|
return api_key
|
||||||
|
|
||||||
def anthropic_fn(self, history, verbose=False):
|
|
||||||
"""
|
|
||||||
Use Anthropic to generate text.
|
|
||||||
"""
|
|
||||||
from anthropic import Anthropic
|
|
||||||
|
|
||||||
client = Anthropic(api_key=self.api_key)
|
|
||||||
system_message = None
|
|
||||||
messages = []
|
|
||||||
for message in history:
|
|
||||||
clean_message = {'role': message['role'], 'content': message['content']}
|
|
||||||
if message['role'] == 'system':
|
|
||||||
system_message = message['content']
|
|
||||||
else:
|
|
||||||
messages.append(clean_message)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = client.messages.create(
|
|
||||||
model=self.model,
|
|
||||||
max_tokens=1024,
|
|
||||||
messages=messages,
|
|
||||||
system=system_message
|
|
||||||
)
|
|
||||||
if response is None:
|
|
||||||
raise Exception("Anthropic response is empty.")
|
|
||||||
thought = response.content[0].text
|
|
||||||
if verbose:
|
|
||||||
print(thought)
|
|
||||||
return thought
|
|
||||||
except Exception as e:
|
|
||||||
raise Exception(f"Anthropic API error: {str(e)}") from e
|
|
||||||
|
|
||||||
def respond(self, history, verbose=True):
|
def respond(self, history, verbose=True):
|
||||||
"""
|
"""
|
||||||
Use the choosen provider to generate text.
|
Use the choosen provider to generate text.
|
||||||
@@ -255,6 +222,38 @@ class Provider:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception(f"OpenAI API error: {str(e)}") from e
|
raise Exception(f"OpenAI API error: {str(e)}") from e
|
||||||
|
|
||||||
|
def anthropic_fn(self, history, verbose=False):
|
||||||
|
"""
|
||||||
|
Use Anthropic to generate text.
|
||||||
|
"""
|
||||||
|
from anthropic import Anthropic
|
||||||
|
|
||||||
|
client = Anthropic(api_key=self.api_key)
|
||||||
|
system_message = None
|
||||||
|
messages = []
|
||||||
|
for message in history:
|
||||||
|
clean_message = {'role': message['role'], 'content': message['content']}
|
||||||
|
if message['role'] == 'system':
|
||||||
|
system_message = message['content']
|
||||||
|
else:
|
||||||
|
messages.append(clean_message)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = client.messages.create(
|
||||||
|
model=self.model,
|
||||||
|
max_tokens=1024,
|
||||||
|
messages=messages,
|
||||||
|
system=system_message
|
||||||
|
)
|
||||||
|
if response is None:
|
||||||
|
raise Exception("Anthropic response is empty.")
|
||||||
|
thought = response.content[0].text
|
||||||
|
if verbose:
|
||||||
|
print(thought)
|
||||||
|
return thought
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"Anthropic API error: {str(e)}") from e
|
||||||
|
|
||||||
def google_fn(self, history, verbose=False):
|
def google_fn(self, history, verbose=False):
|
||||||
"""
|
"""
|
||||||
Use google gemini to generate text.
|
Use google gemini to generate text.
|
||||||
|
|||||||
@@ -0,0 +1,230 @@
|
|||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
from sources.tools.tools import Tools
|
||||||
|
|
||||||
|
class TestToolsParsing(unittest.TestCase):
|
||||||
|
"""
|
||||||
|
Test suite for the Tools class parsing functionality, specifically the load_exec_block method.
|
||||||
|
This method is responsible for extracting code blocks from LLM-generated text.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Set up test fixtures before each test method."""
|
||||||
|
class TestTool(Tools):
|
||||||
|
def execute(self, blocks, safety=False):
|
||||||
|
return "test execution"
|
||||||
|
|
||||||
|
def execution_failure_check(self, output):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def interpreter_feedback(self, output):
|
||||||
|
return "test feedback"
|
||||||
|
|
||||||
|
self.tool = TestTool()
|
||||||
|
self.tool.tag = "python" # Set tag for testing
|
||||||
|
|
||||||
|
def test_load_exec_block_single_block(self):
|
||||||
|
"""Test parsing a single code block from LLM text."""
|
||||||
|
llm_text = """Here's some Python code:
|
||||||
|
```python
|
||||||
|
print("Hello, World!")
|
||||||
|
x = 42
|
||||||
|
```
|
||||||
|
That's the code."""
|
||||||
|
|
||||||
|
blocks, save_path = self.tool.load_exec_block(llm_text)
|
||||||
|
|
||||||
|
self.assertIsNotNone(blocks)
|
||||||
|
self.assertEqual(len(blocks), 1)
|
||||||
|
self.assertEqual(blocks[0], '\nprint("Hello, World!")\nx = 42\n')
|
||||||
|
self.assertIsNone(save_path)
|
||||||
|
|
||||||
|
def test_load_exec_block_multiple_blocks(self):
|
||||||
|
"""Test parsing multiple code blocks from LLM text."""
|
||||||
|
llm_text = """First block:
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
print("First block")
|
||||||
|
```
|
||||||
|
|
||||||
|
Second block:
|
||||||
|
```python
|
||||||
|
import sys
|
||||||
|
print("Second block")
|
||||||
|
```
|
||||||
|
|
||||||
|
Done."""
|
||||||
|
|
||||||
|
blocks, save_path = self.tool.load_exec_block(llm_text)
|
||||||
|
|
||||||
|
self.assertIsNotNone(blocks)
|
||||||
|
self.assertEqual(len(blocks), 2)
|
||||||
|
self.assertEqual(blocks[0], '\nimport os\nprint("First block")\n')
|
||||||
|
self.assertEqual(blocks[1], '\nimport sys\nprint("Second block")\n')
|
||||||
|
self.assertIsNone(save_path)
|
||||||
|
|
||||||
|
def test_load_exec_block_with_save_path(self):
|
||||||
|
"""Test parsing code block with save path specification."""
|
||||||
|
llm_text = """```python
|
||||||
|
save_path: test_file.py
|
||||||
|
import os
|
||||||
|
print("Hello with save path")
|
||||||
|
```"""
|
||||||
|
|
||||||
|
blocks, save_path = self.tool.load_exec_block(llm_text)
|
||||||
|
|
||||||
|
self.assertIsNotNone(blocks)
|
||||||
|
self.assertEqual(len(blocks), 1)
|
||||||
|
self.assertEqual(blocks[0], '\nsave_path: test_file.py\nimport os\nprint("Hello with save path")\n')
|
||||||
|
self.assertIsNone(save_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_exec_block_with_indentation(self):
|
||||||
|
"""Test parsing code blocks with leading whitespace/indentation."""
|
||||||
|
llm_text = """ Here's indented code:
|
||||||
|
```python
|
||||||
|
def hello():
|
||||||
|
print("Hello")
|
||||||
|
return True
|
||||||
|
```
|
||||||
|
End of code."""
|
||||||
|
|
||||||
|
blocks, save_path = self.tool.load_exec_block(llm_text)
|
||||||
|
|
||||||
|
self.assertIsNotNone(blocks)
|
||||||
|
self.assertEqual(len(blocks), 1)
|
||||||
|
expected_code = '\ndef hello():\n print("Hello")\n return True\n'
|
||||||
|
self.assertEqual(blocks[0], expected_code)
|
||||||
|
|
||||||
|
def test_load_exec_block_no_blocks(self):
|
||||||
|
"""Test parsing text with no code blocks."""
|
||||||
|
llm_text = """This is just regular text with no code blocks.
|
||||||
|
There are no python blocks here.
|
||||||
|
Just plain text."""
|
||||||
|
|
||||||
|
blocks, save_path = self.tool.load_exec_block(llm_text)
|
||||||
|
|
||||||
|
self.assertIsNone(blocks)
|
||||||
|
self.assertIsNone(save_path)
|
||||||
|
|
||||||
|
def test_load_exec_block_wrong_tag(self):
|
||||||
|
"""Test parsing text with code blocks but wrong language tag."""
|
||||||
|
llm_text = """```javascript
|
||||||
|
console.log("This is JavaScript, not Python");
|
||||||
|
```"""
|
||||||
|
|
||||||
|
blocks, save_path = self.tool.load_exec_block(llm_text)
|
||||||
|
|
||||||
|
self.assertIsNone(blocks)
|
||||||
|
self.assertIsNone(save_path)
|
||||||
|
|
||||||
|
def test_load_exec_block_incomplete_block(self):
|
||||||
|
"""Test parsing text with incomplete code block (missing closing tag)."""
|
||||||
|
llm_text = """```python
|
||||||
|
print("This block has no closing tag")
|
||||||
|
x = 42"""
|
||||||
|
|
||||||
|
blocks, save_path = self.tool.load_exec_block(llm_text)
|
||||||
|
|
||||||
|
self.assertEqual(blocks, [])
|
||||||
|
self.assertIsNone(save_path)
|
||||||
|
|
||||||
|
def test_load_exec_block_empty_block(self):
|
||||||
|
"""Test parsing empty code block."""
|
||||||
|
llm_text = """```python
|
||||||
|
```"""
|
||||||
|
|
||||||
|
blocks, save_path = self.tool.load_exec_block(llm_text)
|
||||||
|
|
||||||
|
self.assertIsNotNone(blocks)
|
||||||
|
self.assertEqual(len(blocks), 1)
|
||||||
|
self.assertEqual(blocks[0], '\n')
|
||||||
|
|
||||||
|
def test_load_exec_block_mixed_content(self):
|
||||||
|
"""Test parsing text with mixed content including code blocks."""
|
||||||
|
llm_text = """Let me help you with that task.
|
||||||
|
|
||||||
|
First, I'll import the necessary modules:
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
```
|
||||||
|
|
||||||
|
Then I'll define a function:
|
||||||
|
```python
|
||||||
|
def process_data(data):
|
||||||
|
return data.upper()
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, let's use it:
|
||||||
|
```python
|
||||||
|
result = process_data("hello world")
|
||||||
|
print(result)
|
||||||
|
```
|
||||||
|
|
||||||
|
That should work!"""
|
||||||
|
|
||||||
|
blocks, save_path = self.tool.load_exec_block(llm_text)
|
||||||
|
|
||||||
|
self.assertIsNotNone(blocks)
|
||||||
|
self.assertEqual(len(blocks), 3)
|
||||||
|
self.assertEqual(blocks[0], '\nimport os\nimport sys\n')
|
||||||
|
self.assertEqual(blocks[1], '\ndef process_data(data):\n return data.upper()\n')
|
||||||
|
self.assertEqual(blocks[2], '\nresult = process_data("hello world")\nprint(result)\n')
|
||||||
|
|
||||||
|
def test_load_exec_block_with_special_characters(self):
|
||||||
|
"""Test parsing code blocks containing special characters."""
|
||||||
|
llm_text = """```python
|
||||||
|
text = "Hello \"world\" with 'quotes'"
|
||||||
|
regex = r"^\\d+$"
|
||||||
|
path = "C:\\Users\\test\\file.txt"
|
||||||
|
```"""
|
||||||
|
|
||||||
|
blocks, save_path = self.tool.load_exec_block(llm_text)
|
||||||
|
|
||||||
|
self.assertIsNotNone(blocks)
|
||||||
|
self.assertEqual(len(blocks), 1)
|
||||||
|
expected = '\ntext = "Hello "world" with \'quotes\'"\nregex = r"^\\d+$"\npath = "C:\\Users\\test\\file.txt"\n'
|
||||||
|
self.assertEqual(blocks[0], expected)
|
||||||
|
|
||||||
|
def test_load_exec_block_tag_undefined(self):
|
||||||
|
"""Test that assertion error is raised when tag is undefined."""
|
||||||
|
self.tool.tag = "undefined"
|
||||||
|
llm_text = """```python
|
||||||
|
print("test")
|
||||||
|
```"""
|
||||||
|
|
||||||
|
with self.assertRaises(AssertionError):
|
||||||
|
self.tool.load_exec_block(llm_text)
|
||||||
|
|
||||||
|
def test_found_executable_blocks_flag(self):
|
||||||
|
"""Test that the executable blocks found flag is set correctly."""
|
||||||
|
self.assertFalse(self.tool.found_executable_blocks())
|
||||||
|
|
||||||
|
llm_text = """```python
|
||||||
|
print("test")
|
||||||
|
```"""
|
||||||
|
|
||||||
|
blocks, save_path = self.tool.load_exec_block(llm_text)
|
||||||
|
|
||||||
|
self.assertTrue(self.tool.found_executable_blocks())
|
||||||
|
|
||||||
|
self.assertFalse(self.tool.found_executable_blocks())
|
||||||
|
|
||||||
|
def test_get_parameter_value(self):
|
||||||
|
"""Test the get_parameter_value helper method."""
|
||||||
|
block = """param1 = value1
|
||||||
|
param2 = value2
|
||||||
|
some other text
|
||||||
|
param3 = value3"""
|
||||||
|
|
||||||
|
self.assertEqual(self.tool.get_parameter_value(block, "param1"), "value1")
|
||||||
|
self.assertEqual(self.tool.get_parameter_value(block, "param2"), "value2")
|
||||||
|
self.assertEqual(self.tool.get_parameter_value(block, "param3"), "value3")
|
||||||
|
self.assertIsNone(self.tool.get_parameter_value(block, "nonexistent"))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user