Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

changes to tool as per review advice #12

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
from __future__ import annotations

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

from langchain_core.callbacks import CallbackManagerForToolRun
from langchain_core.tools import BaseTool

logger = logging.getLogger(__name__)


class AzureTranslateTool(BaseTool):
"""
A tool that interacts with the Azure Translator API using the SDK.

This tool queries the Azure Translator API to translate text between
languages. It requires an API key and endpoint, which can be set up as
described in the Azure Translator API documentation:
https://learn.microsoft.com/en-us/azure/ai-services/translator/
translator-text-apis?tabs=python
"""

translate_client: Any = None
default_language: str = "en"
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think to be consistent between translate tools (especially if we decide to merge them), lets add the following:

text_translation_key: str = ""
text_translation_endpoint: str = ""
region: str = ""


name: str = "azure_translator_tool"
description: str = (
"A wrapper around Azure Translator API. Useful for translating text between "
"languages. Input must be text (str). Ensure to install the "
"azure-ai-translation-text package."
)

@classmethod
def validate_environment(cls, values: Dict) -> Any:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Missing model_validator (from pydantic import model_validator)

Suggested change
@classmethod
def validate_environment(cls, values: Dict) -> Any:
@model_validator(mode="before")
@classmethod
def validate_environment(cls, values: Dict) -> Any:

"""
Validate that the required environment variables are set, and set up
the client.
"""
try:
from azure.ai.translation.text import TextTranslationClient
from azure.core.credentials import AzureKeyCredential
except ImportError:
raise ImportError(
"azure-ai-translation-text is not installed. "
"Run `pip install azure-ai-translation-text` to install."
)

# Get environment variables
translate_key = os.getenv("AZURE_TRANSLATE_API_KEY")
translate_endpoint = os.getenv("AZURE_TRANSLATE_ENDPOINT")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's leverage the helper function for env vars that the other tools use:

# Get environment variables
azure_translate_key = get_from_dict_or_env(
    values, "text_translation_key", "AZURE_TRANSLATE_API_KEY"
)
azure_translate_endpoint = get_from_dict_or_env(
    values, "text_translation_endpoint", "AZURE_TRANSLATE_ENDPOINT"
)

region = get_from_dict_or_env(
    values, "region", "AZURE_REGION"
)


if not translate_key:
raise ValueError(
"AZURE_TRANSLATE_API_KEY is missing in environment variables"
)
if not translate_endpoint:
raise ValueError(
"AZURE_TRANSLATE_ENDPOINT is missing in environment variables"
)

# Set up the translation client in the values dict
values["translate_client"] = TextTranslationClient(
endpoint=translate_endpoint, credential=AzureKeyCredential(translate_key)
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I needed region to call the client successfully

Suggested change
values["translate_client"] = TextTranslationClient(
endpoint=translate_endpoint, credential=AzureKeyCredential(translate_key)
)
values["translate_client"] = TextTranslationClient(
endpoint=translate_endpoint, credential=AzureKeyCredential(translate_key), region=region
)


return values

def _translate_text(self, text: str, to_language: str = "en") -> str:
"""
Perform text translation using the Azure Translator API.

Args:
text (str): The text to be translated.
to_language (str): The target language to translate to.

Returns:
str: The translation result.
"""
if not text:
raise ValueError("Input text for translation is empty.")

# Ensure that the translation client is initialized
# by validating the environment
if not self.translate_client:
values = self.validate_environment({})
self.translate_client = values["translate_client"]

body = [{"Text": text}]
try:
response = self.translate_client.translate(
body=body, to_language=[to_language]
)
return response[0].translations[0].text
except Exception as e:
raise RuntimeError(f"Translation failed: {e}")

def _run(
self,
query: str,
run_manager: Optional[CallbackManagerForToolRun] = None,
) -> str:
"""
Run the tool to perform translation. Always uses default language.

Args:
query (str): The text to be translated.
run_manager (Optional[CallbackManagerForToolRun]): A callback manager
for tracking the tool run.

Returns:
str: The translated text.
"""
return self._translate_text(query)
170 changes: 170 additions & 0 deletions translate_test.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
{
"cells": [
{
"cell_type": "code",
"id": "initial_id",
"metadata": {
"collapsed": true,
"jupyter": {
"is_executing": true
}
},
"source": [
"\n",
"import os\n",
"from dotenv import load_dotenv\n",
"from libs.community.langchain_community.tools.azure_ai_services.translate_tool import AzureTranslateTool\n",
"from langchain_core.prompts import PromptTemplate\n",
"\n",
"load_dotenv()"
],
"outputs": [],
"execution_count": null
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-10-10T02:47:56.337282Z",
"start_time": "2024-10-10T02:47:56.322998Z"
}
},
"cell_type": "code",
"source": [
"translate_key = os.getenv('AZURE_TRANSLATE_API_KEY')\n",
"translate_endpoint = os.getenv('AZURE_TRANSLATE_ENDPOINT')\n"
],
"id": "d16a2afc75afc1a4",
"outputs": [],
"execution_count": 9
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-10-10T02:47:56.353227Z",
"start_time": "2024-10-10T02:47:56.338283Z"
}
},
"cell_type": "code",
"source": "translator = AzureTranslateTool(translate_key=translate_key, translate_endpoint=translate_endpoint)",
"id": "6a9a508bab82e582",
"outputs": [],
"execution_count": 10
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-10-10T02:47:56.368993Z",
"start_time": "2024-10-10T02:47:56.355229Z"
}
},
"cell_type": "code",
"source": [
"\n",
"prompt = PromptTemplate(\n",
" input_variables=['input'],\n",
" template=\"You are a translation agent. Use the translation tool to translate the following input: {input}\"\n",
")\n"
],
"id": "e4d08ff8441d3ac1",
"outputs": [],
"execution_count": 11
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-10-10T02:47:56.384562Z",
"start_time": "2024-10-10T02:47:56.369994Z"
}
},
"cell_type": "code",
"source": [
"def translate_text(input_text, target_language='es'):\n",
" try:\n",
" # Run the translation\n",
" translated_text = translator._run(input_text, target_language)\n",
Copy link
Collaborator

Choose a reason for hiding this comment

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

We shouldn't call the internal methods. I'd expect a user to invoke the tool directly or use an agent.

translate_tool = AzureTranslateTool(
    text_translation_key=os.environ["AZURE_TRANSLATE_API_KEY"],
    text_translation_endpoint=os.environ["AZURE_TRANSLATE_ENDPOINT"],
    region="westus2",
)
answer = translate_tool.invoke({"query": "hola mundo"})

print(answer)
translate_tool = AzureTranslateTool(
    text_translation_key=os.environ["AZURE_TRANSLATE_API_KEY"],
    text_translation_endpoint=os.environ["AZURE_TRANSLATE_ENDPOINT"],
    region="westus2",
)

tools = [translate_tool]

model = AzureChatOpenAI(
    openai_api_version=os.environ["OPENAI_API_VERSION"],
    azure_deployment=os.environ["COMPLETIONS_MODEL"],
    azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
    api_key=os.environ["AZURE_OPENAI_API_KEY"],
)

agent = create_structured_chat_agent(model, tools, prompt)

agent_executor = AgentExecutor(
    agent=agent, tools=tools, verbose=True, handle_parsing_errors=True
)

text = "hola mundo"

answer = agent_executor.invoke(
    {"input": f"What does the text say in english? : {text}"}
)
print(answer)

"\n",
" # Format the result in a dictionary\n",
" result = {\n",
" 'input': input_text,\n",
" 'output': translated_text\n",
" }\n",
" return result\n",
"\n",
" except Exception as e:\n",
" # Log the error and return an error dictionary\n",
" print(f\"Translation error: {e}\")\n",
" return {'error': str(e)}\n"
],
"id": "4467f2ee18e2201b",
"outputs": [],
"execution_count": 12
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-10-10T02:48:03.660147Z",
"start_time": "2024-10-10T02:48:02.915399Z"
}
},
"cell_type": "code",
"source": [
"# Instantiate the AzureTranslateTool\n",
"translator = AzureTranslateTool()\n",
"\n",
"# Sample text to translate\n",
"sample_text = \"hi how are you?\"\n",
"target_language = 'es'\n",
"\n",
"# Perform translation using the _translate_text method directly\n",
"translated_text = translator._translate_text(sample_text, to_language=target_language)\n",
"\n",
"print(f\"Translated text: {translated_text}\")\n"
],
"id": "bb5b638111ce6ae9",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Translated text: ¿Hola cómo estás?\n"
]
}
],
"execution_count": 14
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-10-10T02:47:57.242259Z",
"start_time": "2024-10-10T02:47:57.227198Z"
}
},
"cell_type": "code",
"source": "",
"id": "a026310fe89600af",
"outputs": [],
"execution_count": 13
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading