diff --git a/core/agents/developer.py b/core/agents/developer.py index cc5536f8e..c2b1e91cd 100644 --- a/core/agents/developer.py +++ b/core/agents/developer.py @@ -145,6 +145,7 @@ async def breakdown_current_iteration(self, review_feedback: Optional[str] = Non user_feedback=user_feedback, user_feedback_qa=None, next_solution_to_try=None, + docs=self.current_state.docs, ) .assistant(description) .template("parse_task") @@ -180,10 +181,7 @@ async def breakdown_current_task(self) -> AgentResponse: llm = self.get_llm() convo = AgentConvo(self).template( - "breakdown", - task=task, - iteration=None, - current_task_index=current_task_index, + "breakdown", task=task, iteration=None, current_task_index=current_task_index, docs=self.current_state.docs ) response: str = await llm(convo) diff --git a/core/agents/external_docs.py b/core/agents/external_docs.py index 8226a68bc..7c7f1b0c9 100644 --- a/core/agents/external_docs.py +++ b/core/agents/external_docs.py @@ -131,7 +131,7 @@ async def _fetch_snippets(self, queries: dict[str, list[str]]) -> list[tuple]: reqs = [] ordered_keys = [] for docset_key, qs in queries.items(): - reqs.append(client.get(url, params={"q": qs, "doc_key": docset_key})) + reqs.append(client.get(url, params={"q": qs, "doc_key": docset_key, "num_results": 3})) ordered_keys.append(docset_key) try: @@ -156,5 +156,5 @@ async def _store_docs(self, snippets: list[tuple], available_docsets: list[tuple for docset_key, snip in snippets: docs.append({"key": docset_key, "desc": docsets_dict[docset_key], "snippets": snip}) - self.next_state.current_task["docs"] = docs + self.next_state.docs = docs self.next_state.flag_tasks_as_modified() diff --git a/core/agents/orchestrator.py b/core/agents/orchestrator.py index 632015429..b27f48582 100644 --- a/core/agents/orchestrator.py +++ b/core/agents/orchestrator.py @@ -190,8 +190,7 @@ def create_agent(self, prev_response: Optional[AgentResponse]) -> BaseAgent: # Ask the Tech Lead to break down the initial project or feature into tasks and apply project template return TechLead(self.state_manager, self.ui, process_manager=self.process_manager) - current_task_docs = state.current_task.get("docs") if state.current_task else None - if state.current_task and current_task_docs is None: + if state.current_task and state.docs is None: return ExternalDocumentation(self.state_manager, self.ui) # Current task status must be checked before Developer is called because we might want diff --git a/core/db/migrations/versions/b760f66138c0_add_docs_column_to_project_states.py b/core/db/migrations/versions/b760f66138c0_add_docs_column_to_project_states.py new file mode 100644 index 000000000..5ab1ee599 --- /dev/null +++ b/core/db/migrations/versions/b760f66138c0_add_docs_column_to_project_states.py @@ -0,0 +1,34 @@ +"""Add docs column to project_states + +Revision ID: b760f66138c0 +Revises: f352dbe45751 +Create Date: 2024-06-08 10:00:44.222099 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "b760f66138c0" +down_revision: Union[str, None] = "f352dbe45751" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table("project_states", schema=None) as batch_op: + batch_op.add_column(sa.Column("docs", sa.JSON(), nullable=True)) + + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table("project_states", schema=None) as batch_op: + batch_op.drop_column("docs") + + # ### end Alembic commands ### diff --git a/core/db/models/project_state.py b/core/db/models/project_state.py index 70bc78e19..df9506d76 100644 --- a/core/db/models/project_state.py +++ b/core/db/models/project_state.py @@ -53,6 +53,7 @@ class ProjectState(Base): iterations: Mapped[list[dict]] = mapped_column(default=list) relevant_files: Mapped[Optional[list[str]]] = mapped_column(default=None) modified_files: Mapped[dict] = mapped_column(default=dict) + docs: Mapped[Optional[list[dict]]] = mapped_column(default=None) run_command: Mapped[Optional[str]] = mapped_column() action: Mapped[Optional[str]] = mapped_column() @@ -219,6 +220,7 @@ async def create_next_state(self) -> "ProjectState": files=[], relevant_files=deepcopy(self.relevant_files), modified_files=deepcopy(self.modified_files), + docs=deepcopy(self.docs), run_command=self.run_command, ) @@ -256,6 +258,7 @@ def complete_task(self): self.iterations = [] self.relevant_files = None self.modified_files = {} + self.docs = None flag_modified(self, "tasks") if not self.unfinished_tasks and self.unfinished_epics: diff --git a/core/prompts/external-docs/create_docs_queries.prompt b/core/prompts/external-docs/create_docs_queries.prompt index 6023d26cd..b3e8f1759 100644 --- a/core/prompts/external-docs/create_docs_queries.prompt +++ b/core/prompts/external-docs/create_docs_queries.prompt @@ -11,4 +11,4 @@ Here's an example for React API documentation: We have additional documentation from "{{ short_description }}" that might be useful for completing this task. -Now, give me a summary of what specifically from the {{ short_description }} you think would be useful for completing this task. Please provide only the topics of interest, no additional text. Only return the topics relevant to the actual implementation, NOT the topics related to library installation and setup, environment setup, database setup and similar. Return the topics in JSON format, as a list of strings, WITHOUT any additional formatting such as backticks, bullets and similar. Return a maximum of 5 topics you think would be most useful. +Now, give me a summary of what specifically from the {{ short_description }} you think would be useful for completing this task. Please provide only the topics of interest, no additional text. Only return the topics relevant to the actual implementation, NOT the topics related to library installation and setup, environment setup, database setup and similar. Return the topics in JSON format, as a list of strings, WITHOUT any additional formatting such as backticks, bullets and similar. Return a maximum of 3 topics you think would be most useful. diff --git a/core/prompts/partials/doc_snippets.prompt b/core/prompts/partials/doc_snippets.prompt index 0714f4d35..ba7955e29 100644 --- a/core/prompts/partials/doc_snippets.prompt +++ b/core/prompts/partials/doc_snippets.prompt @@ -1,8 +1,8 @@ -{% if task.docs %} +{% if docs is defined and docs %} We have some some documentation snippets that might be helpful while working on this task, we will now list those. ---START_OF_DOCUMENTATION_SNIPPETS--- -{% for d in task.docs %} +{% for d in docs %} Documentation snippets from {{ d.desc }}: {% for snippet in d.snippets %} {{ snippet }} diff --git a/core/prompts/troubleshooter/iteration.prompt b/core/prompts/troubleshooter/iteration.prompt index d99c30999..5c7cf74bc 100644 --- a/core/prompts/troubleshooter/iteration.prompt +++ b/core/prompts/troubleshooter/iteration.prompt @@ -39,9 +39,7 @@ Focus on solving this issue in the following way: {{ next_solution_to_try }} ``` {% endif %} -{% with task=current_task %} - {% include "partials/doc_snippets.prompt" %} -{% endwith %} +{% include "partials/doc_snippets.prompt" %} Now, you have to debug this issue and comply with the additional user feedback. **IMPORTANT** diff --git a/tests/agents/test_external_docs.py b/tests/agents/test_external_docs.py index 5fc2522f2..d36ce0a8d 100644 --- a/tests/agents/test_external_docs.py +++ b/tests/agents/test_external_docs.py @@ -18,7 +18,7 @@ async def test_stores_documentation_snippets_for_task(agentcontext): side_effect=[SelectedDocsets(docsets=["vuejs-api-ref"]), DocQueries(queries=["VueJS component model"])] ) await ed.run() - assert ed.next_state.current_task["docs"][0]["key"] == "vuejs-api-ref" + assert ed.next_state.docs[0]["key"] == "vuejs-api-ref" @pytest.mark.asyncio @@ -32,4 +32,4 @@ async def test_continues_without_docs_if_api_is_down(agentcontext): with patch("httpx.Client.get", side_effect=HTTPError("Failed")): await ed.run() - assert ed.next_state.current_task["docs"] == [] + assert ed.next_state.docs == []