mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-09 16:18:30 +00:00
Address PR feedback: Improve I18N implementation with centralized instantiation and better validation
Co-Authored-By: Joe Moura <joao@crewai.com>
This commit is contained in:
@@ -617,7 +617,7 @@ class Crew(BaseModel):
|
||||
self._interpolate_inputs(inputs)
|
||||
self._set_tasks_callbacks()
|
||||
|
||||
i18n = I18N(prompt_file=self.prompt_file, language=self.language)
|
||||
i18n = self._create_i18n()
|
||||
|
||||
for agent in self.agents:
|
||||
agent.i18n = i18n
|
||||
@@ -758,8 +758,17 @@ class Crew(BaseModel):
|
||||
self._create_manager_agent()
|
||||
return self._execute_tasks(self.tasks)
|
||||
|
||||
def _create_i18n(self) -> I18N:
|
||||
"""
|
||||
Create an I18N instance with the crew's configuration.
|
||||
|
||||
Returns:
|
||||
I18N: An internationalization instance configured with the crew's settings.
|
||||
"""
|
||||
return I18N(prompt_file=self.prompt_file, language=self.language)
|
||||
|
||||
def _create_manager_agent(self):
|
||||
i18n = I18N(prompt_file=self.prompt_file, language=self.language)
|
||||
i18n = self._create_i18n()
|
||||
if self.manager_agent is not None:
|
||||
self.manager_agent.allow_delegation = True
|
||||
manager = self.manager_agent
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import json
|
||||
import os
|
||||
from typing import Dict, Optional, Union
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import Dict, Literal, Optional, Union
|
||||
|
||||
from pydantic import BaseModel, Field, PrivateAttr, model_validator
|
||||
|
||||
"""Internationalization support for CrewAI prompts and messages."""
|
||||
|
||||
SUPPORTED_LANGUAGES = Literal["en", "fr", "es", "pt"]
|
||||
|
||||
class I18N(BaseModel):
|
||||
"""Handles loading and retrieving internationalized prompts."""
|
||||
_prompts: Dict[str, Dict[str, str]] = PrivateAttr()
|
||||
@@ -15,28 +19,55 @@ class I18N(BaseModel):
|
||||
)
|
||||
language: Optional[str] = Field(
|
||||
default="en",
|
||||
description="Language to use for translations",
|
||||
description="Language to use for translations. Defaults to English.",
|
||||
)
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def validate_language(cls, data):
|
||||
"""
|
||||
Validate the language parameter.
|
||||
|
||||
If the language is not supported, it will fall back to English.
|
||||
"""
|
||||
if isinstance(data, dict) and "language" in data:
|
||||
lang = data["language"]
|
||||
if lang and lang not in ["en", "fr", "es", "pt"]:
|
||||
print(f"Warning: Language '{lang}' not supported. Falling back to English.")
|
||||
data["language"] = "en"
|
||||
return data
|
||||
|
||||
@model_validator(mode="after")
|
||||
def load_prompts(self) -> "I18N":
|
||||
"""Load prompts from a JSON file."""
|
||||
"""
|
||||
Load prompts from a JSON file.
|
||||
|
||||
If prompt_file is provided, loads from that file.
|
||||
Otherwise, attempts to load from the language-specific translation file.
|
||||
Falls back to English if the specified language file doesn't exist.
|
||||
|
||||
Raises:
|
||||
Exception: If the prompt file is not found or contains invalid JSON.
|
||||
|
||||
Returns:
|
||||
I18N: The instance with loaded prompts.
|
||||
"""
|
||||
try:
|
||||
if self.prompt_file:
|
||||
with open(self.prompt_file, "r", encoding="utf-8") as f:
|
||||
self._prompts = json.load(f)
|
||||
else:
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
base_path = Path(__file__).parent / "../translations"
|
||||
lang = self.language or "en"
|
||||
prompts_path = os.path.join(dir_path, f"../translations/{lang}.json")
|
||||
lang_file = base_path / f"{lang}.json"
|
||||
|
||||
if not os.path.exists(prompts_path):
|
||||
prompts_path = os.path.join(dir_path, "../translations/en.json")
|
||||
if not lang_file.exists():
|
||||
lang_file = base_path / "en.json"
|
||||
|
||||
with open(prompts_path, "r", encoding="utf-8") as f:
|
||||
with open(lang_file.resolve(), "r", encoding="utf-8") as f:
|
||||
self._prompts = json.load(f)
|
||||
except FileNotFoundError:
|
||||
raise Exception(f"Prompt file '{self.prompt_file}' not found.")
|
||||
raise Exception(f"Prompt file '{self.prompt_file or lang_file}' not found.")
|
||||
except json.JSONDecodeError:
|
||||
raise Exception("Error decoding JSON from the prompts file.")
|
||||
|
||||
@@ -46,15 +77,31 @@ class I18N(BaseModel):
|
||||
return self
|
||||
|
||||
def slice(self, slice: str) -> str:
|
||||
"""Get a slice prompt by key."""
|
||||
return self.retrieve("slices", slice)
|
||||
|
||||
def errors(self, error: str) -> str:
|
||||
"""Get an error message by key."""
|
||||
return self.retrieve("errors", error)
|
||||
|
||||
def tools(self, tool: str) -> Union[str, Dict[str, str]]:
|
||||
"""Get a tool prompt by key."""
|
||||
return self.retrieve("tools", tool)
|
||||
|
||||
def retrieve(self, kind, key) -> str:
|
||||
def retrieve(self, kind: str, key: str) -> str:
|
||||
"""
|
||||
Retrieve a prompt by section and key.
|
||||
|
||||
Args:
|
||||
kind: The section in the prompts file (e.g., "slices", "errors")
|
||||
key: The specific key within the section
|
||||
|
||||
Returns:
|
||||
The prompt text
|
||||
|
||||
Raises:
|
||||
Exception: If the prompt is not found
|
||||
"""
|
||||
try:
|
||||
return self._prompts[kind][key]
|
||||
except Exception as _:
|
||||
|
||||
Reference in New Issue
Block a user