Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ repos:
- id: yamllint
exclude: pre-commit-config.yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.12.3"
rev: "v0.12.4"
hooks:
- id: ruff-format
- id: ruff-check
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,36 @@
import os

from google.adk.agents import Agent, LoopAgent
from google.adk.tools import FunctionTool
from manugen_ai.agents.meta_agent import ResilientToolAgent
from manugen_ai.tools.tools import exit_loop
from manugen_ai.agents.meta_agent import StopChecker
from manugen_ai.utils import get_llm

# Environment-driven model names
MODEL_NAME = os.environ.get("MANUGENAI_MODEL_NAME")
LLM = get_llm(MODEL_NAME)
COMPLETION_PHRASE = "THE AGENT HAS COMPLETED THE TASK."
COMPLETION_PHRASE = "TASK COMPLETED!"

agent_review_loop = Agent(
model=LLM,
name="review_and_decide",
description="Critique full markdown and decide if it's final.",
instruction="""
description="Critique full markdown and decide if it's finalized.",
instruction=f"""
You receive content from the user.

Your job:
1. Provide a concise bullet-list of any changes needed.
2. If **no** changes are needed (content is publication-ready), call the tool `exit_loop` instead of returning bullets.
- Use clear, actionable language.
- Focus on clarity, coherence, and scientific rigor.
- If changes are needed, return a list of feedback bullets.
- Don't go overboard, try to keep it concise and reasonable when it comes to updates.
- Do NOT provide guidance about new topics. You are to stick to only the topic areas within the content.
2. If **no** changes are needed (content is publication-ready), response ONLY with "{COMPLETION_PHRASE}" instead of returning bullets.

Return either:
- A JSON list of feedback bullets,
- Or invoke `exit_loop` (via the FunctionTool) to end the loop.
Return ONLY ONE of the following.
DO NOT return both!
1. A list of feedback bullets for use with revision.
2. Or respond with ONLY "{COMPLETION_PHRASE}" to indicate the content is ready for publication
If you respond with "{COMPLETION_PHRASE}", do not include any additional text or commentary.
""",
tools=[FunctionTool(func=exit_loop)],
output_key="feedback",
)

Expand All @@ -39,14 +43,20 @@
model=LLM,
name="refine_manuscript",
description="Apply feedback to revise the manuscript.",
instruction="""
instruction=f"""
You receive:
- Content from the user.
- feedback: a list of bullets in {feedback}
- feedback: a list of bullets in {{feedback}}

Revise the content accordingly and return *only* updated content.
You MUST return actual content, not instructions or commentary about the content.
Do not add additional headers which are not in the original content.
Do not include "{COMPLETION_PHRASE}" in the output.
Do not include a header about the feedback.
Do NOT return additional commentary, JSON, or metadata.
ONLY return the revised content.
The revised content should be in markdown format and not include any additional
wrappers (such as backticks ```) or formatting besides what was provided in the original content.
""",
output_key="refined_md",
)
Expand All @@ -55,10 +65,11 @@
root_agent = LoopAgent(
name="reviewer_agent",
description="Iteratively review and refine the manuscript until publication-ready.",
# Use ResilientToolAgent so missing-tool errors auto-retry up to N times
sub_agents=[
ResilientToolAgent(agent_review_loop, max_retries=2),
ResilientToolAgent(agent_refine, max_retries=2),
agent_review_loop,
agent_refine,
# use stopchecker to check if the work is completed.
StopChecker(context_variable="feedback", completion_phrase=COMPLETION_PHRASE),
],
max_iterations=5, # safety cap
)
2 changes: 1 addition & 1 deletion packages/manugen-ai/src/manugen_ai/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def get_llm(model_name: str, **kwargs):
elif model_name.startswith("gemini-"):
return model_name

raise ValueError(f"Unknown model name: {model_name}")
raise ValueError(f"Unknown model name: {model_name if model_name else '<empty>'}")


def prepare_ollama_models_for_adk_state() -> None:
Expand Down
7 changes: 7 additions & 0 deletions packages/manugen-ai/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@
conftest for pytest fixutres and related
"""

import os
import pathlib
import tempfile
from typing import Any, Generator

import pytest


@pytest.fixture(autouse=True, scope="session")
def set_model_name_env():
os.environ["MANUGENAI_MODEL_NAME"] = "openai/llama3.2:3b"
os.environ["MANUGENAI_FIGURE_MODEL_NAME"] = "openai/llama3.2:3b"


@pytest.fixture
def temp_markdown_dir() -> Generator[
pathlib.Path,
Expand Down
719 changes: 719 additions & 0 deletions packages/manugen-ai/tests/reports/revise_content.ipynb

Large diffs are not rendered by default.

46 changes: 44 additions & 2 deletions packages/manugen-ai/tests/test_agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""

import pytest
from manugen_ai.agents.capitalizer.agent import root_agent
from manugen_ai.agents.capitalizer.agent import root_agent as capitalizer_agent
from manugen_ai.utils import run_agent_workflow


Expand All @@ -17,7 +17,7 @@ async def test_agent_capitalizer():
# retry 5 times
for attempt in range(5):
_, session_state, _ = await run_agent_workflow(
agent=root_agent,
agent=capitalizer_agent,
prompt="""
this is a sentence to correct
""",
Expand All @@ -32,3 +32,45 @@ async def test_agent_capitalizer():
# Final attempt failed, raise assertion
assert "output" in session_state.keys()
assert session_state["output"] == expected_output


@pytest.mark.asyncio
async def test_agent_reviewer():
"""
Test the reviewer agent to ensure it can loop and improve text
"""

# localized imports to help with agent env setting
from manugen_ai.agents.ai_science_writer.sub_agents.reviewer.agent import (
COMPLETION_PHRASE as review_agent_COMPLETION_PHRASE,
)
from manugen_ai.agents.ai_science_writer.sub_agents.reviewer.agent import (
root_agent as review_agent,
)

# we attempt multiple times to ensure the agent can loop and improve the text
for attempt in range(5):
_, session_state, output_events = await run_agent_workflow(
agent=review_agent,
prompt="""
Please improve the following text:

It was a dark and stormy night.
""",
app_name="app",
user_id="user",
session_id="0001",
# note: use `uv run pytest -s` to view the verbose stdout
verbose=True,
)

# check that we received feedback and that the completion phrase was used.
if (
"feedback" in session_state
and session_state["feedback"] == review_agent_COMPLETION_PHRASE
):
break
if attempt == 4:
raise AssertionError(
"Final attempt failed, completion phrase not found or does not match expected output."
)
4 changes: 4 additions & 0 deletions packages/manugen-ai/tests/test_tools.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
"""
Tests for custom tools
"""

import pathlib
from types import SimpleNamespace

Expand Down