Skip to content

feat: Add A2A Translator Agent with text and URL translation capabili… - #111

Open
Pasindu-Jayasundara wants to merge 1 commit into
Nasiko-Labs:mainfrom
Pasindu-Jayasundara:pasindu-jayasundara
Open

feat: Add A2A Translator Agent with text and URL translation capabili…#111
Pasindu-Jayasundara wants to merge 1 commit into
Nasiko-Labs:mainfrom
Pasindu-Jayasundara:pasindu-jayasundara

Conversation

@Pasindu-Jayasundara

Copy link
Copy Markdown

…ties

  • Implemented OpenAIAgentExecutor for handling OpenAI-based agent execution.
  • Created TranslatorToolset for translating text and web content.
  • Added models for translation requests and responses using Pydantic.
  • Developed methods for translating text, extracting content from URLs, and detecting languages.
  • Set up Dockerfile and docker-compose for easy deployment.
  • Included README with installation instructions, usage examples, and configuration details.
  • Added .gitignore to exclude unnecessary files from version control.
  • Created supporting files and structure for the A2A Translator Agent.

…ties

- Implemented OpenAIAgentExecutor for handling OpenAI-based agent execution.
- Created TranslatorToolset for translating text and web content.
- Added models for translation requests and responses using Pydantic.
- Developed methods for translating text, extracting content from URLs, and detecting languages.
- Set up Dockerfile and docker-compose for easy deployment.
- Included README with installation instructions, usage examples, and configuration details.
- Added .gitignore to exclude unnecessary files from version control.
- Created supporting files and structure for the A2A Translator Agent.
Copilot AI review requested due to automatic review settings May 16, 2026 21:55

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces three new A2A-compatible agents under agents/ (Translator, GitHub, Compliance Checker), each shipped with its own src/ Python package, Dockerfile, docker-compose.yml, AgentCard.json, README and pyproject.toml. It also disables the healthcheck for an existing service in docker-compose.local.yml and renames the root virtual project package in uv.lock from arithmic-nasiko to nasiko. The PR description only mentions the Translator agent, but the diff is much broader than that.

Changes:

  • Add A2A Translator agent (OpenAI-driven executor, Google-Translate-backed toolset, language detection).
  • Add A2A GitHub agent and A2A Compliance Checker agent with similar OpenAI executor scaffolding.
  • Disable healthcheck for an existing service in docker-compose.local.yml and rename the root project package in uv.lock.

Reviewed changes

Copilot reviewed 37 out of 41 changed files in this pull request and generated 21 comments.

Show a summary per file
File Description
uv.lock Renames root virtual project arithmic-nasikonasiko; unrelated to PR description
docker-compose.local.yml Disables healthcheck for an existing service (regression)
agents/a2a-translator/v1.0.0/src/translator_toolset.py Translator tooling using Google's internal endpoint; mixed sync/async API
agents/a2a-translator/v1.0.0/src/openai_agent_executor.py OpenAI tool-calling loop; has duplicate-complete() bug and schema-inference limitations
agents/a2a-translator/v1.0.0/src/openai_agent.py System prompt + tool registration
agents/a2a-translator/v1.0.0/src/main.py Server entrypoint; references likely-nonexistent default model IDs
agents/a2a-translator/v1.0.0/{Dockerfile,docker-compose.yml,AgentCard.json,pyproject.toml,README.md,run_with_phoenix.sh,.gitignore} Deployment + docs; placeholder Phoenix key, unused googletrans dep, wrong outputModes
agents/a2a-github-agent/v1.0.0/src/main.py Entrypoint; imports non-existent poc_obser and has flawed provider selection
agents/a2a-github-agent/v1.0.0/src/github_toolset.py PyGithub-based tooling; naive vs. aware datetime comparison bug
agents/a2a-github-agent/v1.0.0/src/openai_agent_executor.py / openai_agent.py Same executor pattern as translator
agents/a2a-github-agent/v1.0.0/{Dockerfile,docker-compose.yml,AgentCard.json,pyproject.toml,README.md,run_with_phoenix.sh,.gitignore} Deployment + docs; malformed environment: in compose, README points to wrong repo
agents/a2a-compliance-checker/v1.0.0/src/policy_agent.py References missing BaseAgent / base_agent module — agent won't start
agents/a2a-compliance-checker/v1.0.0/src/compliance_toolset.py Shared "a2a_session" and shared document_text across requests
agents/a2a-compliance-checker/v1.0.0/src/agent.py Leftover translation agent code in compliance package
agents/a2a-compliance-checker/v1.0.0/src/openai_agent_executor.py / openai_agent.py / main.py / models.py / tools.py Executor + scaffolding mirroring the other agents
agents/a2a-compliance-checker/v1.0.0/{Dockerfile,docker-compose.yml,AgentCard.json,pyproject.toml,README.md,.gitignore} Deployment + docs; project name compliance-checker2, port mismatch with AgentCard
Comments suppressed due to low confidence (5)

agents/a2a-translator/v1.0.0/src/translator_toolset.py:299

  • translate_text is async, but translate_url and detect_language are sync and perform blocking I/O (requests.get, BeautifulSoup parsing, blocking call to Google Translate). When called from within the async _process_request loop in openai_agent_executor.py (line 110: result = method(**function_args)), these will block the event loop for the duration of the network request, defeating the async server. Either make them async (and offload via run_in_executor, like translate_text does), or have the executor consistently dispatch sync tools to a thread.
    agents/a2a-translator/v1.0.0/src/openai_agent_executor.py:221
  • _extract_function_schema does not consult typing generics or Optional/X | None, so parameters like source_language: str | None = None are typed "string" in the OpenAI schema but never marked as required (correct), while a list/dict parameter declared via PEP 585 generics (list[str], dict[str, Any]) would be typed as the default "string" because param.annotation == list is False for list[str]. For the current toolsets parameters are mostly str/int, but as new tools get added this will silently produce wrong schemas. Use typing.get_origin/get_args for proper inference.
    agents/a2a-translator/v1.0.0/src/openai_agent_executor.py:181
  • inspect is already imported at the top of the file (line 3). Re-importing it inside this method shadows nothing but is unnecessary and out of place; drop the inner import inspect.
    agents/a2a-translator/v1.0.0/src/openai_agent_executor.py:177
  • When max_iterations is reached, this branch sends a final error artifact and calls task_updater.complete(), but the prior iteration of the loop already called task_updater.complete() (line 156) on a successful path is fine — however if the loop exits the while normally via break after the success path on line 157, iteration is still <= max_iterations, yet this if iteration >= max_iterations (line 170) will be True whenever the loop completed its 10th iteration successfully, causing a duplicate complete() and a spurious "exceeded the maximum number of iterations" message after a valid final answer. Use else on the while loop, or set a completed flag.
    agents/a2a-translator/v1.0.0/src/translator_toolset.py:251
  • Translation result text is truncated to 5000 characters via slicing before being sent for translation (line 250-251 appends a literal "..." to whatever happens to be the 5000th character — potentially mid-byte in a multi-byte UTF-8 sequence when the underlying string was just decoded). Python str slicing is by codepoints so it won't break encoding, but it will routinely cut in the middle of a sentence/word and feed that to Google Translate, producing low-quality output and a trailing literal "..." in the source language. Consider sentence-aware truncation, or surfacing a warning to the user that the page was truncated.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +3 to +22
from base_agent import BaseAgent

logger = logging.getLogger(__name__)


class PolicyAgent(BaseAgent):
"""Handles LLM-based Policy Inquiries"""

def get_response(self, query: str, session_id: str) -> str:
logger.info(
f"PolicyAgent generating response for session {session_id}, query='{query}'"
)
system_prompt = f"""
You are a specialized Policy Compliance Checker Agent.
Your expertise is analyzing documents and content for policy violations and compliance issues.

DOCUMENT UNDER REVIEW:
\"\"\"
{self.document_parser.document_text}
\"\"\"
Comment on lines +21 to +24
from poc_obser.tracing_utils import bootstrap_tracing

bootstrap_tracing(project_name="a2a-github-agent")

Comment on lines +1 to +76
"""
Core agent logic for translation.
"""

import logging
import os
from typing import List, Dict, Any, Optional

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import AgentExecutor, create_tool_calling_agent


from tools import extract_web_text

logger = logging.getLogger(__name__)


def _create_llm() -> ChatOpenAI:
"""Create LLM instance, supporting OpenAI and MiniMax providers."""
if os.getenv("MINIMAX_API_KEY") and not os.getenv("OPENAI_API_KEY"):
return ChatOpenAI(
model=os.getenv("MINIMAX_MODEL", "MiniMax-M2.7"),
temperature=1.0,
api_key=os.getenv("MINIMAX_API_KEY"),
base_url=os.getenv("MINIMAX_BASE_URL", "https://api.minimax.io/v1"),
)
return ChatOpenAI(model="gpt-4o", temperature=0)


class Agent:
def __init__(self):
# Initialize your agent
self.name = "Translation Agent"

# Initialize Tools
self.tools = [extract_web_text]

# Initialize LangChain components
self.llm = _create_llm()

# System prompt tailored for text-to-text translation
self.system_prompt = """You are a helpful assistant whose primary objective is to help the user with language translation.

RULES:
- If the user provides a URL, use the 'extract_web_text' tool to get the content, then translate the extracted text.
- Detect the source language and the target language from the user's request.
- If the user specifies a target language, translate the text to that language.
- If the user provides text without specifying a target language, default to translating it to English (if it's not English) or ask for clarification if ambiguous.
- Translate the text fully and accurately.
- Preserve the original meaning and tone.
- Do NOT add explanations or commentary unless the translation requires context notes (which should be minimal).

RESPONSE FORMAT:
- Provide only the translated text.
"""

self.prompt = ChatPromptTemplate.from_messages(
[
("system", self.system_prompt),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
]
)

# Create Tool Calling Agent
agent = create_tool_calling_agent(self.llm, self.tools, self.prompt)
self.agent_executor = AgentExecutor(agent=agent, tools=self.tools, verbose=True)

def process_message(self, message_text: str) -> str:
"""
Process the incoming message using LangChain.
"""
logger.info(f"Processing message: {message_text[:50]}...")
result = self.agent_executor.invoke({"input": message_text})
return result["output"]
Comment thread docker-compose.local.yml
Comment on lines 83 to +85
healthcheck:
test: ["CMD-SHELL", "python -c \"import requests; requests.get('http://localhost:8000/api/v1/healthcheck').raise_for_status()\" || exit 1"]
interval: 30s
timeout: 10s
retries: 3
disable: true

Comment on lines +93 to +129
def _translate_with_google(
self, text: str, src_lang: str, dest_lang: str
) -> tuple[str, str]:
"""Translate text using Google Translate API directly"""
try:
# Google Translate URL
url = "https://translate.googleapis.com/translate_a/single"

params = {
"client": "gtx",
"sl": src_lang,
"tl": dest_lang,
"dt": "t",
"q": text,
}

response = self.session.get(url, params=params, timeout=10)
response.raise_for_status()

result = response.json()

# Extract translated text
translated_text = ""
if result and len(result) > 0 and result[0]:
for sentence in result[0]:
if sentence and len(sentence) > 0:
translated_text += sentence[0]

# Extract detected source language
detected_src = src_lang
if len(result) > 2 and result[2]:
detected_src = result[2]

return translated_text.strip(), detected_src

except Exception as e:
raise Exception(f"Translation failed: {str(e)}")
Comment on lines +88 to +94
- a2a-sdk: A2A framework for agent communication
- googletrans: Google Translate API wrapper
- beautifulsoup4: HTML parsing for web content extraction
- langdetect: Language detection library
- requests: HTTP client for web scraping
- OpenAI: For agent conversation handling

Comment on lines +44 to +50
# Clone the repository
git clone https://github.com/a2aproject/a2a-samples.git
cd a2a-samples/samples/python/agents/github-agent

# Create virtual environment
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
Comment on lines +85 to +91
messages.append(
{
"role": "assistant",
"content": message.content,
"tool_calls": message.tool_calls,
}
)
Comment on lines +11 to +13
ports:
- "5000"
tty: true

ENV PYTHONUNBUFFERED=1

CMD ["python", "__main__.py", "--host", "0.0.0.0", "--port", "5000", "--mongo-url", "mongodb://agents-mongo:27017", "--db-name", "compliance-checker-a2a"] No newline at end of file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants