diff --git a/cogsol/core/migrations.py b/cogsol/core/migrations.py index f538420..75afa3a 100644 --- a/cogsol/core/migrations.py +++ b/cogsol/core/migrations.py @@ -166,7 +166,7 @@ def diff_states( ) else: # Cognitive API entities (agents) - for entity in ["retrieval_tools", "tools", "lessons", "faqs", "fixed_responses", "agents"]: + for entity in ["retrieval_tools", "tools", "agents", "lessons", "faqs", "fixed_responses"]: operations.extend( _diff_bucket( entity, diff --git a/cogsol/management/commands/makemigrations.py b/cogsol/management/commands/makemigrations.py index b77ae4b..2674713 100644 --- a/cogsol/management/commands/makemigrations.py +++ b/cogsol/management/commands/makemigrations.py @@ -4,7 +4,10 @@ from pathlib import Path from typing import Any -from cogsol import __version__ +try: + from cogsol import __version__ +except ImportError: + __version__ = "unknown" from cogsol.core import migrations as migutils from cogsol.core.loader import collect_content_definitions, collect_definitions from cogsol.management.base import BaseCommand diff --git a/cogsol/management/commands/startproject.py b/cogsol/management/commands/startproject.py index 865315f..d06742a 100644 --- a/cogsol/management/commands/startproject.py +++ b/cogsol/management/commands/startproject.py @@ -33,23 +33,24 @@ def main(): TOOLS_PY = """\ from cogsol.tools import BaseTool, tool_params -# -# class ExampleTool(BaseTool): -# description = "Demo tool that echoes the provided text." -# -# @tool_params( -# text={"description": "Text to echo", "type": "string", "required": True}, -# count={"description": "Times to repeat", "type": "integer", "required": False}, -# ) -# def run(self, chat=None, data=None, secrets=None, log=None, text: str = "", count: int = 1): -# \"\"\" -# text: Text to echo back. -# count: Times to repeat the text. -# \"\"\" -# message = " ".join([text] * max(1, int(count))) -# # chat/data/secrets/log are available per platform docs -# response = message -# return response + + +class ExampleTool(BaseTool): + description = "Demo tool that echoes the provided text." + + @tool_params( + text={"description": "Text to echo", "type": "string", "required": True}, + count={"description": "Times to repeat", "type": "integer", "required": False}, + ) + def run(self, chat=None, data=None, secrets=None, log=None, text: str = "", count: int = 1): + \"\"\" + text: Text to echo back. + count: Times to repeat the text. + \"\"\" + message = " ".join([text] * max(1, int(count))) + # chat/data/secrets/log are available per platform docs + response = message + return response """ SEARCHES_PY = """\ diff --git a/tests/test_agents.py b/tests/test_agents.py index 0536554..146d4cb 100644 --- a/tests/test_agents.py +++ b/tests/test_agents.py @@ -8,7 +8,13 @@ from cogsol.agents import BaseAgent, genconfigs, optimizations from cogsol.core.loader import collect_definitions from cogsol.core.migrations import diff_states, empty_state -from cogsol.db.migrations import AlterField, CreateRetrievalTool +from cogsol.db.migrations import ( + AlterField, + CreateAgent, + CreateLesson, + CreateRetrievalTool, + CreateTool, +) class TestBaseAgent: @@ -141,6 +147,106 @@ def test_faq_change_creates_faq_alter(self): assert agent_faq_ops == [] +class TestOperationOrdering: + """Tests for correct dependency ordering of migration operations.""" + + def test_agent_created_before_lessons(self): + """CreateAgent should appear before CreateLesson in migration operations.""" + with tempfile.TemporaryDirectory() as tmpdir: + project_path = Path(tmpdir) + agents_path = project_path / "agents" + agent_pkg = agents_path / "assistant" + agent_pkg.mkdir(parents=True) + + (agents_path / "__init__.py").write_text("", encoding="utf-8") + (agents_path / "tools.py").write_text("", encoding="utf-8") + (agent_pkg / "__init__.py").write_text("", encoding="utf-8") + (agent_pkg / "agent.py").write_text( + """ +from cogsol.agents import BaseAgent + + +class GreetingLesson: + name = "Greeting" + content = "Always greet the user." + + +class AssistantAgent(BaseAgent): + system_prompt = "You are a helpful assistant." + lessons = [GreetingLesson()] + + class Meta: + name = "AssistantAgent" + chat_name = "Assistant" +""", + encoding="utf-8", + ) + + defs = collect_definitions(project_path, "agents") + ops = diff_states(empty_state(), defs, app="agents") + + create_agent_indices = [i for i, op in enumerate(ops) if isinstance(op, CreateAgent)] + create_lesson_indices = [i for i, op in enumerate(ops) if isinstance(op, CreateLesson)] + + assert create_agent_indices, "Expected at least one CreateAgent operation" + assert create_lesson_indices, "Expected at least one CreateLesson operation" + assert max(create_agent_indices) < min( + create_lesson_indices + ), "CreateAgent operations must come before CreateLesson operations" + + def test_tools_created_before_agents(self): + """CreateTool should appear before CreateAgent in migration operations.""" + with tempfile.TemporaryDirectory() as tmpdir: + project_path = Path(tmpdir) + agents_path = project_path / "agents" + agent_pkg = agents_path / "assistant" + agent_pkg.mkdir(parents=True) + + (agents_path / "__init__.py").write_text("", encoding="utf-8") + (agents_path / "tools.py").write_text( + """ +from cogsol.tools import BaseTool + + +class MyTool(BaseTool): + name = "my_tool" + description = "A test tool." + parameters = [] + + def run(self, **kwargs): + return "ok" +""", + encoding="utf-8", + ) + (agent_pkg / "__init__.py").write_text("", encoding="utf-8") + (agent_pkg / "agent.py").write_text( + """ +from cogsol.agents import BaseAgent + + +class AssistantAgent(BaseAgent): + system_prompt = "You are a helpful assistant." + + class Meta: + name = "AssistantAgent" + chat_name = "Assistant" +""", + encoding="utf-8", + ) + + defs = collect_definitions(project_path, "agents") + ops = diff_states(empty_state(), defs, app="agents") + + create_tool_indices = [i for i, op in enumerate(ops) if isinstance(op, CreateTool)] + create_agent_indices = [i for i, op in enumerate(ops) if isinstance(op, CreateAgent)] + + assert create_tool_indices, "Expected at least one CreateTool operation" + assert create_agent_indices, "Expected at least one CreateAgent operation" + assert max(create_tool_indices) < min( + create_agent_indices + ), "CreateTool operations must come before CreateAgent operations" + + class TestRetrievalTools: """Tests for retrieval tool definitions."""