Spaces:
Running
Running
Commit
·
d7527be
1
Parent(s):
1d7c988
add_first
Browse files- app.py +70 -0
- languageexport/__pycache__/__init__.cpython-312.pyc +0 -0
- languageexport/__pycache__/crew.cpython-312.pyc +0 -0
- languageexport/__pycache__/flow.cpython-312.pyc +0 -0
- languageexport/__pycache__/main.cpython-312.pyc +0 -0
- languageexport/config/agents.yaml +19 -0
- languageexport/config/tasks.yaml +39 -0
- languageexport/crew.py +44 -0
- languageexport/flow.py +46 -0
- languageexport/main.py +82 -0
- languageexport/tools/__init__.py +0 -0
- languageexport/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- languageexport/tools/__pycache__/custom_tool.cpython-312.pyc +0 -0
- languageexport/tools/custom_tool.py +7 -0
- pyproject.toml +26 -0
- requirements.txt +7 -0
- uv.lock +0 -0
app.py
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import pandas as pd
|
3 |
+
from languageexport.crew import LanguagesExport
|
4 |
+
import tempfile
|
5 |
+
import os
|
6 |
+
|
7 |
+
def process_excel(file, output_name):
|
8 |
+
if not file or not output_name.strip():
|
9 |
+
return "Please upload a file and provide a valid name.", None, None
|
10 |
+
|
11 |
+
try:
|
12 |
+
df = pd.read_excel(file.name)
|
13 |
+
table = [df.columns.tolist()] + df.values.tolist()
|
14 |
+
|
15 |
+
result = LanguagesExport().crew().kickoff(inputs={"data": table})
|
16 |
+
result_df = pd.DataFrame(data=result["data"][1:], columns=result["data"][0])
|
17 |
+
|
18 |
+
temp_dir = tempfile.mkdtemp()
|
19 |
+
output_file_path = os.path.join(temp_dir, f"{output_name.strip()}.xlsx")
|
20 |
+
result_df.to_excel(output_file_path, index=False)
|
21 |
+
|
22 |
+
return "✅ Export completed successfully!", result_df, output_file_path
|
23 |
+
|
24 |
+
except Exception as e:
|
25 |
+
return f"❌ Error: {str(e)}", None, None
|
26 |
+
|
27 |
+
|
28 |
+
def show_input_table(file):
|
29 |
+
if file is None:
|
30 |
+
return gr.update(visible=True, value=None)
|
31 |
+
try:
|
32 |
+
df = pd.read_excel(file.name)
|
33 |
+
return gr.update(visible=True, value=df)
|
34 |
+
except Exception as e:
|
35 |
+
return gr.update(visible=True, value=None)
|
36 |
+
|
37 |
+
with gr.Blocks() as demo:
|
38 |
+
gr.Markdown("# 📄 Language Export Assistant")
|
39 |
+
gr.Markdown("Upload an Excel file, define the export filename, and run the export process.")
|
40 |
+
|
41 |
+
with gr.Row():
|
42 |
+
file_input = gr.File(label="Upload Excel File", file_types=[".xlsx", ".xls"])
|
43 |
+
file_name = gr.Textbox(label="Output file name (without extension)", placeholder="e.g. translated_table")
|
44 |
+
|
45 |
+
run_button = gr.Button("Run Export")
|
46 |
+
|
47 |
+
status_output = gr.Textbox(label="Status", interactive=False)
|
48 |
+
|
49 |
+
# Luôn hiển thị layout, chỉ update nội dung
|
50 |
+
with gr.Row():
|
51 |
+
table_input = gr.Dataframe(label="Input Table")
|
52 |
+
table_output = gr.Dataframe(label="Result Table")
|
53 |
+
|
54 |
+
download_output = gr.File(label="Download Result File", visible=False)
|
55 |
+
|
56 |
+
file_input.change(fn=show_input_table, inputs=file_input, outputs=table_input)
|
57 |
+
|
58 |
+
def on_run(file, name):
|
59 |
+
status, df, path = process_excel(file, name)
|
60 |
+
is_valid_df = df is not None and not df.empty
|
61 |
+
return (
|
62 |
+
status,
|
63 |
+
gr.update(value=df if is_valid_df else None),
|
64 |
+
gr.update(value=path if path else None, visible=path is not None),
|
65 |
+
)
|
66 |
+
|
67 |
+
run_button.click(on_run, inputs=[file_input, file_name], outputs=[status_output, table_output, download_output])
|
68 |
+
|
69 |
+
if __name__ == "__main__":
|
70 |
+
demo.launch(share=True)
|
languageexport/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (157 Bytes). View file
|
|
languageexport/__pycache__/crew.cpython-312.pyc
ADDED
Binary file (2.73 kB). View file
|
|
languageexport/__pycache__/flow.cpython-312.pyc
ADDED
Binary file (3.34 kB). View file
|
|
languageexport/__pycache__/main.cpython-312.pyc
ADDED
Binary file (4.07 kB). View file
|
|
languageexport/config/agents.yaml
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
translator_researcher:
|
2 |
+
role: >
|
3 |
+
Translation Data Specialist & Localization Engineer
|
4 |
+
(Expert Level)
|
5 |
+
goal: >
|
6 |
+
Ensure the entire 2D translation table is accurately completed:
|
7 |
+
- Automatically generate and refine any missing English phrases from the 'String' keys.
|
8 |
+
- Accurately fill all remaining translation cells with fluent, natural translations in Title Case, without copying English phrases directly.
|
9 |
+
- Preserve the table's structure exactly (no new columns, no reordering).
|
10 |
+
backstory: >
|
11 |
+
You are a translation and localization expert with 20 years of experience working on multilingual software projects.
|
12 |
+
You have reviewed tens of thousands of translation entries across over 40 languages.
|
13 |
+
You master the principles of high-quality localization and take extra care to avoid machine-like or unnatural results.
|
14 |
+
Your working style is meticulous—reviewing every cell without omission, never altering or adding columns unnecessarily.
|
15 |
+
You always double-check every translation
|
16 |
+
You never leave cells half-done.
|
17 |
+
You **never** assume a missing language exists.
|
18 |
+
Your translations feel human — thoughtful, localized, and clean.
|
19 |
+
|
languageexport/config/tasks.yaml
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
research_task:
|
2 |
+
description: >
|
3 |
+
You will be given {data}, a 2D Python List[List[str|None]] structured as follows:
|
4 |
+
- Row 0 is the header: the first column is always 'String', and the rest are language names.
|
5 |
+
- Rows 1…n contain translation data for each key.
|
6 |
+
YOU MUST identify the list of language columns **explicitly** from the header row for translating later.
|
7 |
+
ONLY and must translate into languages that are actually present — do NOT assume or invent or copy (for example copy English phase to French language).
|
8 |
+
|
9 |
+
Your responsibilities:
|
10 |
+
1. Parse the headers to identify all **existing** language columns.
|
11 |
+
→ DO NOT assume the existence of any column unless explicitly present.
|
12 |
+
→ DO NOT create new columns (e.g., 'English').
|
13 |
+
|
14 |
+
2. For each row:
|
15 |
+
a. Derive the English phrase from the 'String' key:
|
16 |
+
- Remove the prefix 'STR_'
|
17 |
+
- Replace all underscores with spaces
|
18 |
+
- Convert the phrase to Title Case
|
19 |
+
b. Use this derived phrase as the translation base.
|
20 |
+
c. IMPORTANT STEP: For each language cell:
|
21 |
+
- If the cell is:
|
22 |
+
• Empty
|
23 |
+
• Null
|
24 |
+
• Whitespace
|
25 |
+
• **OR exactly matches `english_phrase`** (**CRITICAL**: this is not a valid translation!)
|
26 |
+
→ Then translate `english_phrase` into the TARGET LANGUAGE.
|
27 |
+
→ The translation must:
|
28 |
+
• Be natural and fluent
|
29 |
+
• Match Title Case
|
30 |
+
• Contain **no** extra punctuation, quotes, or added words
|
31 |
+
- Otherwise: leave the cell unchanged.
|
32 |
+
expected_output: >
|
33 |
+
A 2D Python `List[List[str]]` of identical shape where:
|
34 |
+
- All originally missing translation cells are now correctly filled
|
35 |
+
- Table structure is preserved exactly (no added columns, no reordering)
|
36 |
+
- Existing non-empty translations remain unchanged
|
37 |
+
- All derived English phrases are used strictly as translation bases
|
38 |
+
- All new translations follow capitalization and output rules strictly
|
39 |
+
agent: translator_researcher
|
languageexport/crew.py
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from crewai import Agent, Crew, Process, Task,LLM
|
2 |
+
from crewai.project import CrewBase, agent, crew, task
|
3 |
+
import os
|
4 |
+
from languageexport.tools.custom_tool import SaveTranslationInput
|
5 |
+
import pandas as pd
|
6 |
+
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
|
7 |
+
os.environ["OPENROUTER_API_KEY"] = os.getenv("OPENROUTER_API_KEY")
|
8 |
+
name_0="openrouter/tngtech/deepseek-r1t-chimera:free"
|
9 |
+
name_z="openrouter/meta-llama/llama-4-scout:free"
|
10 |
+
name="openai/gpt-4.1-mini-2025-04-14"
|
11 |
+
llm=LLM(
|
12 |
+
model=name_z,
|
13 |
+
temperature=0.0,
|
14 |
+
)
|
15 |
+
import yaml
|
16 |
+
|
17 |
+
@CrewBase
|
18 |
+
class LanguagesExport():
|
19 |
+
def __init__(self):
|
20 |
+
agents_config = yaml.safe_load(open(r'D:\CrewAI\crew\languageexport\src\languageexport\config\agents.yaml', encoding='utf-8'))
|
21 |
+
tasks_config = yaml.safe_load(open(r'D:\CrewAI\crew\languageexport\src\languageexport\config\tasks.yaml', encoding='utf-8'))
|
22 |
+
@agent
|
23 |
+
def translator_researcher(self) -> Agent:
|
24 |
+
return Agent(
|
25 |
+
config=self.agents_config['translator_researcher'],
|
26 |
+
llm=llm,
|
27 |
+
verbose=True
|
28 |
+
)
|
29 |
+
|
30 |
+
@task
|
31 |
+
def research_task(self) -> Task:
|
32 |
+
return Task(
|
33 |
+
config=self.tasks_config['research_task'],
|
34 |
+
output_pydantic=SaveTranslationInput,
|
35 |
+
)
|
36 |
+
|
37 |
+
@crew
|
38 |
+
def crew(self) -> Crew:
|
39 |
+
return Crew(
|
40 |
+
agents=[self.translator_researcher()],
|
41 |
+
tasks=[self.research_task()],
|
42 |
+
process=Process.sequential,
|
43 |
+
verbose=True
|
44 |
+
)
|
languageexport/flow.py
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from crewai.flow.flow import Flow,listen,start
|
2 |
+
import json
|
3 |
+
import os
|
4 |
+
from typing import List, Dict,Any
|
5 |
+
from pydantic import BaseModel, Field
|
6 |
+
from crewai.flow.flow import Flow, listen, start
|
7 |
+
import pandas as pd
|
8 |
+
from languageexport.crew import LanguagesExport
|
9 |
+
class OutputFile(Flow):
|
10 |
+
@start()
|
11 |
+
def get_data(self):
|
12 |
+
path = input("Enter path to your Excel file (e.g., full_translations.xlsx): ").strip()
|
13 |
+
if not os.path.exists(path):
|
14 |
+
raise FileNotFoundError(f"File not found: {path}")
|
15 |
+
df = pd.read_excel(path)
|
16 |
+
print(f"Loaded {path} with shape {df.shape}")
|
17 |
+
table = [df.columns.tolist()] + df.values.tolist()
|
18 |
+
self.state['table'] = table
|
19 |
+
return table
|
20 |
+
|
21 |
+
@listen(get_data)
|
22 |
+
def ask_filename(self, table: List[List[Any]]):
|
23 |
+
filename = input("Enter desired output filename (with .xlsx extension): ").strip()
|
24 |
+
if not filename.lower().endswith('.xlsx'):
|
25 |
+
filename += '.xlsx'
|
26 |
+
self.state['filename'] = filename
|
27 |
+
return table
|
28 |
+
|
29 |
+
@listen(ask_filename)
|
30 |
+
def create_file(self, table: List[List[Any]]):
|
31 |
+
filename = self.state['filename']
|
32 |
+
result = LanguagesExport().crew().kickoff(inputs={'data': table, 'filename': filename})
|
33 |
+
self.state['result'] = result
|
34 |
+
print(f"Agent returned: {result}")
|
35 |
+
if os.path.exists(filename):
|
36 |
+
print(f"Output file created: {filename}")
|
37 |
+
else:
|
38 |
+
print(f"Expected output file not found: {filename}")
|
39 |
+
return filename
|
40 |
+
|
41 |
+
@listen(create_file)
|
42 |
+
def show_file(self, filename: str):
|
43 |
+
df = pd.read_excel(filename)
|
44 |
+
print("✅ Translated content:")
|
45 |
+
print(df)
|
46 |
+
return df
|
languageexport/main.py
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python
|
2 |
+
import sys
|
3 |
+
import warnings
|
4 |
+
|
5 |
+
from datetime import datetime
|
6 |
+
from typing import List, Dict, Any
|
7 |
+
|
8 |
+
import os
|
9 |
+
|
10 |
+
import json
|
11 |
+
from pydantic import BaseModel, Field
|
12 |
+
from languageexport.flow import OutputFile
|
13 |
+
from languageexport.crew import LanguagesExport
|
14 |
+
import pandas as pd
|
15 |
+
import tkinter as Tk
|
16 |
+
from tkinter import Tk
|
17 |
+
|
18 |
+
from tkinter import filedialog
|
19 |
+
from tkinter import messagebox
|
20 |
+
from tkinter.filedialog import askopenfilename, asksaveasfilename
|
21 |
+
warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd")
|
22 |
+
|
23 |
+
def run():
|
24 |
+
"""
|
25 |
+
Run the crew.
|
26 |
+
"""
|
27 |
+
Tk().withdraw()
|
28 |
+
|
29 |
+
# Mở hộp thoại chọn file
|
30 |
+
print("Vui lòng chọn file Excel đầu vào")
|
31 |
+
|
32 |
+
file_path = askopenfilename(
|
33 |
+
title="Chọn file Excel đầu vào",
|
34 |
+
filetypes=[("Excel files", "*.xlsx *.xls")]
|
35 |
+
)
|
36 |
+
print(f"đường dẫn:{file_path}")
|
37 |
+
file_name=input("Enter the file name: ")
|
38 |
+
file_name_save=f"{file_name}.xlsx"
|
39 |
+
df= pd.read_excel(file_path)
|
40 |
+
table = [df.columns.tolist()] + df.values.tolist()
|
41 |
+
print(table)
|
42 |
+
rs=LanguagesExport().crew().kickoff(inputs={'data':table})
|
43 |
+
df = pd.DataFrame(data=rs["data"][1:], columns=rs["data"][0])
|
44 |
+
df.to_excel(file_name_save, index=False)
|
45 |
+
|
46 |
+
def train():
|
47 |
+
"""
|
48 |
+
Train the crew for a given number of iterations.
|
49 |
+
"""
|
50 |
+
|
51 |
+
inputs = {
|
52 |
+
'data':'table'
|
53 |
+
}
|
54 |
+
try:
|
55 |
+
LanguagesExport().crew().train(n_iterations=int(sys.argv[1]), filename=sys.argv[2], inputs=inputs)
|
56 |
+
|
57 |
+
except Exception as e:
|
58 |
+
raise Exception(f"An error occurred while training the crew: {e}")
|
59 |
+
|
60 |
+
def replay():
|
61 |
+
"""
|
62 |
+
Replay the crew execution from a specific task.
|
63 |
+
"""
|
64 |
+
try:
|
65 |
+
LanguagesExport().crew().replay(task_id=sys.argv[1])
|
66 |
+
|
67 |
+
except Exception as e:
|
68 |
+
raise Exception(f"An error occurred while replaying the crew: {e}")
|
69 |
+
|
70 |
+
def test():
|
71 |
+
"""
|
72 |
+
Test the crew execution and returns the results.
|
73 |
+
"""
|
74 |
+
inputs = {
|
75 |
+
"topic": "AI LLMs",
|
76 |
+
"current_year": str(datetime.now().year)
|
77 |
+
}
|
78 |
+
try:
|
79 |
+
LanguagesExport().crew().test(n_iterations=int(sys.argv[1]), openai_model_name=sys.argv[2], inputs=inputs)
|
80 |
+
|
81 |
+
except Exception as e:
|
82 |
+
raise Exception(f"An error occurred while testing the crew: {e}")
|
languageexport/tools/__init__.py
ADDED
File without changes
|
languageexport/tools/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (163 Bytes). View file
|
|
languageexport/tools/__pycache__/custom_tool.cpython-312.pyc
ADDED
Binary file (820 Bytes). View file
|
|
languageexport/tools/custom_tool.py
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from crewai.tools import BaseTool
|
2 |
+
from typing import Type
|
3 |
+
from pydantic import BaseModel, Field
|
4 |
+
from typing import List, Any,Annotated
|
5 |
+
import pandas as pd
|
6 |
+
class SaveTranslationInput(BaseModel):
|
7 |
+
data: List[List[Any]] = Field(..., description="2D list representing the full translation table. The first row is the header.",strict=True)
|
pyproject.toml
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[project]
|
2 |
+
name = "languageexport"
|
3 |
+
version = "0.1.0"
|
4 |
+
description = "LanguageExport using crewAI"
|
5 |
+
authors = [{ name = "Your Name", email = "you@example.com" }]
|
6 |
+
requires-python = ">=3.10,<3.13"
|
7 |
+
dependencies = [
|
8 |
+
"crewai[tools]>=0.114.0,<1.0.0",
|
9 |
+
"gradio>=5.25.2",
|
10 |
+
"pandas>=2.2.3",
|
11 |
+
"tk>=0.1.0",
|
12 |
+
]
|
13 |
+
|
14 |
+
[project.scripts]
|
15 |
+
languageexport = "languageexport.main:run"
|
16 |
+
run_crew = "languageexport.main:run"
|
17 |
+
train = "languageexport.main:train"
|
18 |
+
replay = "languageexport.main:replay"
|
19 |
+
test = "languageexport.main:test"
|
20 |
+
|
21 |
+
[build-system]
|
22 |
+
requires = ["hatchling"]
|
23 |
+
build-backend = "hatchling.build"
|
24 |
+
|
25 |
+
[tool.crewai]
|
26 |
+
type = "crew"
|
requirements.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
crewai[tools]>=0.114.0,<1.0.0
|
2 |
+
gradio>=5.25.2
|
3 |
+
pandas>=2.2.3
|
4 |
+
tk
|
5 |
+
openpyxl
|
6 |
+
pyyaml
|
7 |
+
numpy
|
uv.lock
ADDED
The diff for this file is too large to render.
See raw diff
|
|