diff --git a/ra_aid/agent_backends/ciayn_agent.py b/ra_aid/agent_backends/ciayn_agent.py index 7a5f70e..855eb57 100644 --- a/ra_aid/agent_backends/ciayn_agent.py +++ b/ra_aid/agent_backends/ciayn_agent.py @@ -620,6 +620,8 @@ class CiaynAgent: self.chat_history.append(HumanMessage(content=base_prompt)) full_history = self._trim_chat_history(initial_messages, self.chat_history) response = self.model.invoke([self.sys_message] + full_history) + print("RESPONSE") + print(response.content) # Check if the response is empty or doesn't contain a valid tool call if not response.content or not response.content.strip(): diff --git a/ra_aid/models_params.py b/ra_aid/models_params.py index d7019de..3e08fad 100644 --- a/ra_aid/models_params.py +++ b/ra_aid/models_params.py @@ -165,6 +165,15 @@ models_params = { "latency_coefficient": DEFAULT_BASE_LATENCY, }, }, + "openai-compatible": { + "qwen-qwq-32b": { + "token_limit": 130000, + "think_tag": True, + "supports_temperature": True, + "latency_coefficient": DEFAULT_BASE_LATENCY, + "max_tokens": 64000, + } + }, "azure_openai": { "gpt-3.5-turbo-0125": { "token_limit": 16385, diff --git a/ra_aid/text/__init__.py b/ra_aid/text/__init__.py index 0b66541..3ab76b6 100644 --- a/ra_aid/text/__init__.py +++ b/ra_aid/text/__init__.py @@ -1,3 +1,3 @@ -from .processing import truncate_output +from .processing import truncate_output, extract_think_tag -__all__ = ["truncate_output"] +__all__ = ["truncate_output", "extract_think_tag"] \ No newline at end of file diff --git a/ra_aid/text/processing.py b/ra_aid/text/processing.py index 65279ac..aae86c5 100644 --- a/ra_aid/text/processing.py +++ b/ra_aid/text/processing.py @@ -1,4 +1,5 @@ -from typing import Optional +from typing import Optional, Tuple +import re def truncate_output(output: str, max_lines: Optional[int] = 5000) -> str: @@ -41,3 +42,29 @@ def truncate_output(output: str, max_lines: Optional[int] = 5000) -> str: # Combine message with remaining lines return truncation_msg + "".join(truncated_lines) + + +def extract_think_tag(text: str) -> Tuple[Optional[str], str]: + """Extract content from the first ... tag at the start of a string. + + Args: + text: Input string that may contain think tags + + Returns: + A tuple containing: + - The extracted content from the first think tag (None if no tag found) + - The remaining string after the first think tag (or the original string if no tag found) + """ + # Pattern to match think tags at the start of the string + pattern = r'^\s*(.*?)' + match = re.search(pattern, text, re.DOTALL) + + if match: + think_content = match.group(1) + # Get the index where the think tag ends + end_index = match.end() + # Extract the remaining text + remaining_text = text[end_index:] + return think_content, remaining_text + else: + return None, text \ No newline at end of file diff --git a/tests/data/think-tag/sample_1.txt b/tests/data/think-tag/sample_1.txt new file mode 100644 index 0000000..8269340 --- /dev/null +++ b/tests/data/think-tag/sample_1.txt @@ -0,0 +1,63 @@ + +Okay, the user wants me to write a "hello world" program in C++. Let me think about the steps needed. + +First, since it's a new project, there are no existing files. So I need to create a main C++ file. The standard entry point for C++ is a main.cpp or something similar. Let's name it main.cpp. + +The code for "Hello World" in C++ typically includes the iostream header and uses std::cout. So the code would be: + +#include +using namespace std; + +int main() { + cout << "Hello, world!" << endl; + return 0; +} + +Wait, but maybe I should check if there's a standard way. Alternatively, sometimes people use std::cout directly instead of the using directive. Maybe the better practice is to avoid the using namespace std; for clarity, so perhaps rewrite it as: + +#include +int main() { + std::cout << "Hello, world!" << std::endl; + return 0; +} + +Hmm, but the user said to keep it simple. The first version is simpler for a beginner example. But maybe the second is better for standards. Let me think. Since the user said "keep it simple", the first version is okay, but I should make sure that the code compiles. + +Next, the user is in a new project directory, so I need to create the main.cpp file. The function to use is request_task_implementation, which takes a task spec. The task spec should be a markdown detailing the steps. Wait, looking back at the available functions, the request_task_implementation function requires a task specification in markdown. So I need to structure that. + +The task spec should describe creating the main.cpp file with the code. So the task_spec would be something like "Create a C++ file named main.cpp with the Hello World program. The program should include iostream, use std::cout to output 'Hello, world!' and end with a return 0; statement in main()". + +Wait, but the user might prefer the using namespace std; to simplify. Let me check again. Since it's a simple example, using the using namespace is acceptable for brevity. So the code would be: + +#include +using namespace std; + +int main() { + cout << "Hello, world!" << endl; + return 0; +} + +Yes, that's standard. Now, to implement this, the correct function to use is request_task_implementation with that code. So the tool call would be to request_task_implementation with the task spec as a markdown string. + +Alternatively, maybe I should first check if there's any existing files, but since it's a new project with 0 files, listing directories might be redundant. The efficiency guidelines say to avoid listing empty directories. So instead, proceed directly to creating the file. + +Therefore, the correct tool call here is to call request_task_implementation with the task specification to create main.cpp with the code. + + +request_task_implementation('''Create a C++ source file named main.cpp containing a standard "Hello World" program. + +Steps: +1. Create file main.cpp +2. Add the following content: +#include +using namespace std; + +int main() { + cout << "Hello, world!" << endl; + return 0; +} + +Acceptance Criteria: +- The program compiles without errors using g++ main.cpp -o hello +- Running ./hello outputs exactly "Hello, world!" followed by a newline +''') \ No newline at end of file diff --git a/tests/ra_aid/text/__init__.py b/tests/ra_aid/text/__init__.py new file mode 100644 index 0000000..62272da --- /dev/null +++ b/tests/ra_aid/text/__init__.py @@ -0,0 +1 @@ +# This file makes the directory a Python package. \ No newline at end of file diff --git a/tests/ra_aid/text/test_processing.py b/tests/ra_aid/text/test_processing.py new file mode 100644 index 0000000..8b84838 --- /dev/null +++ b/tests/ra_aid/text/test_processing.py @@ -0,0 +1,102 @@ +import os +import pytest +from ra_aid.text.processing import extract_think_tag + +def test_basic_extraction(): + """Test basic extraction of think tag content.""" + content = "This is a testRemaining content" + expected_extracted = "This is a test" + expected_remaining = "Remaining content" + + extracted, remaining = extract_think_tag(content) + + assert extracted == expected_extracted + assert remaining == expected_remaining + +def test_multiline_extraction(): + """Test extraction of multiline think tag content.""" + content = "Line 1\nLine 2\nLine 3Remaining content" + expected_extracted = "Line 1\nLine 2\nLine 3" + expected_remaining = "Remaining content" + + extracted, remaining = extract_think_tag(content) + + assert extracted == expected_extracted + assert remaining == expected_remaining + +def test_multiple_think_tags(): + """Test that only the first think tag is extracted.""" + content = "First tagMiddleSecond tagEnd" + expected_extracted = "First tag" + expected_remaining = "MiddleSecond tagEnd" + + extracted, remaining = extract_think_tag(content) + + assert extracted == expected_extracted + assert remaining == expected_remaining + +def test_no_think_tag(): + """Test behavior when no think tag is present.""" + content = "This is a string without a think tag" + + extracted, remaining = extract_think_tag(content) + + assert extracted is None + assert remaining == content + +def test_empty_think_tag(): + """Test extraction of an empty think tag.""" + content = "Remaining content" + expected_extracted = "" + expected_remaining = "Remaining content" + + extracted, remaining = extract_think_tag(content) + + assert extracted == expected_extracted + assert remaining == expected_remaining + +def test_whitespace_handling(): + """Test whitespace handling in think tag extraction.""" + content = " \n Content with whitespace \n Remaining content" + expected_extracted = " \n Content with whitespace \n " + expected_remaining = "Remaining content" + + extracted, remaining = extract_think_tag(content) + + assert extracted == expected_extracted + assert remaining == expected_remaining + +def test_tag_not_at_start(): + """Test behavior when think tag is not at the start of the string.""" + content = "Some content before Think contentRemaining content" + + extracted, remaining = extract_think_tag(content) + + assert extracted is None + assert remaining == content + +def test_sample_data(): + """Test extraction using sample data from tests/data/think-tag/sample_1.txt.""" + # Get the absolute path to the sample file + current_dir = os.path.dirname(os.path.abspath(__file__)) + sample_file_path = os.path.join(current_dir, '..', '..', 'data', 'think-tag', 'sample_1.txt') + + # Read the sample data + with open(sample_file_path, 'r', encoding='utf-8') as f: + sample_data = f.read() + + # Extract the think tag + extracted, remaining = extract_think_tag(sample_data) + + # Check that extraction worked + assert extracted is not None + assert "Okay, the user wants me to write a" in extracted + assert "return 0;" in extracted + + # Check that we got the think tag content without the tags + assert not extracted.startswith("") + assert not extracted.endswith("") + + # Check that the remaining content doesn't contain the think tag + assert "" not in remaining + assert "" not in remaining \ No newline at end of file