|
| 1 | +#!/usr/bin/env python3 |
| 2 | +"""CLI wrapper for AI translation - runs AI in separate process without interactive input""" |
| 3 | + |
| 4 | +import subprocess |
| 5 | +import sys |
| 6 | +import os |
| 7 | + |
| 8 | + |
| 9 | +def run_ai_via_subprocess(headword_id: int, mode: str, synonyms: bool = False, provider: str = "openai") -> str: |
| 10 | + """ |
| 11 | + Run AI translation via subprocess to avoid signal conflicts. |
| 12 | + |
| 13 | + Args: |
| 14 | + headword_id: Headword ID to translate |
| 15 | + mode: "meaning" or "note" |
| 16 | + provider: AI provider ("openai" or "deepseek") |
| 17 | + |
| 18 | + Returns: |
| 19 | + AI-generated suggestion or error message |
| 20 | + """ |
| 21 | + try: |
| 22 | + # Create a simple script that recreates the AI logic without interactive input |
| 23 | + script_content = f''' |
| 24 | +import sys |
| 25 | +import os |
| 26 | +
|
| 27 | +# Add project paths |
| 28 | +project_root = "{os.path.dirname(os.path.dirname(__file__))}" |
| 29 | +sys.path.insert(0, project_root) |
| 30 | +
|
| 31 | +# Import required modules |
| 32 | +from db.db_helpers import get_db_session |
| 33 | +from db.models import DpdHeadword |
| 34 | +from tools.paths import ProjectPaths |
| 35 | +from tools.paths_dps import DPSPaths |
| 36 | +from tools.ai_related import ( |
| 37 | + load_translation_examples, |
| 38 | + replace_abbreviations, |
| 39 | + handle_ai_response, |
| 40 | + get_ai_client, |
| 41 | + generate_messages_for_meaning, |
| 42 | + generate_messages_for_notes |
| 43 | +) |
| 44 | +
|
| 45 | +# Initialize paths and database |
| 46 | +pth = ProjectPaths() |
| 47 | +dpspth = DPSPaths() |
| 48 | +db_session = get_db_session(pth.dpd_db_path) |
| 49 | +
|
| 50 | +# Recreate the working function logic |
| 51 | +def fetch_id(db_session, id_to_check): |
| 52 | + if not id_to_check: |
| 53 | + return None |
| 54 | + query = db_session.query(DpdHeadword).filter(DpdHeadword.id == id_to_check).first() |
| 55 | + return query |
| 56 | +
|
| 57 | +# Get headword data |
| 58 | +pali_word = fetch_id(db_session, {headword_id}) |
| 59 | +if pali_word: |
| 60 | + lemma_1 = pali_word.lemma_1 |
| 61 | + meaning = pali_word.meaning_1 |
| 62 | + pos = pali_word.pos |
| 63 | + grammar = pali_word.grammar |
| 64 | + sentence = pali_word.example_1 |
| 65 | + notes = pali_word.notes |
| 66 | +
|
| 67 | + # Load translation examples and prepare grammar |
| 68 | + pos_example_map = load_translation_examples(dpspth) |
| 69 | + translation_example = pos_example_map.get(pos, "") |
| 70 | +
|
| 71 | + if "{provider}" == "openai": |
| 72 | + # model = "gpt-4.1-mini" |
| 73 | + model = "gpt-4.1" |
| 74 | + elif "{provider}" == "deepseek": |
| 75 | + model = "deepseek-chat" |
| 76 | + grammar_orig = grammar |
| 77 | + grammar = replace_abbreviations(grammar) |
| 78 | +
|
| 79 | + # Generate appropriate messages |
| 80 | + if "{mode}" == "meaning": |
| 81 | + messages = generate_messages_for_meaning(lemma_1, grammar, meaning, sentence, translation_example, {synonyms}) |
| 82 | + elif "{mode}" == "note": |
| 83 | + messages = generate_messages_for_notes(lemma_1, grammar, notes) |
| 84 | +
|
| 85 | + # Get AI client and handle response |
| 86 | + client = get_ai_client("{provider}") |
| 87 | + suggestion, error_string = handle_ai_response(client, messages, model, "{provider}") |
| 88 | +
|
| 89 | + if error_string: |
| 90 | + print(f"AI Error: {{error_string}}") |
| 91 | + elif suggestion: |
| 92 | + suggestion_str = suggestion.get("content", "") if suggestion is not None else "" |
| 93 | + print(suggestion_str) |
| 94 | + else: |
| 95 | + print("No response from AI") |
| 96 | +else: |
| 97 | + print(f"Error: Headword {{headword_id}} not found in database") |
| 98 | +''' |
| 99 | + |
| 100 | + # Run the script as subprocess |
| 101 | + result = subprocess.run( |
| 102 | + [sys.executable, "-c", script_content], |
| 103 | + capture_output=True, |
| 104 | + text=True, |
| 105 | + timeout=120, # 2 minute timeout |
| 106 | + cwd=os.path.dirname(os.path.dirname(__file__)) # Run from project root |
| 107 | + ) |
| 108 | + |
| 109 | + # Debug: Print full output for troubleshooting |
| 110 | + print(f"DEBUG - Subprocess return code: {result.returncode}") |
| 111 | + print(f"DEBUG - Subprocess stdout: {result.stdout}") |
| 112 | + print(f"DEBUG - Subprocess stderr: {result.stderr}") |
| 113 | + |
| 114 | + if result.returncode == 0: |
| 115 | + # Success - return the output |
| 116 | + return result.stdout.strip() |
| 117 | + else: |
| 118 | + # Error - return both stdout and stderr for debugging |
| 119 | + return f"AI Error (return code {result.returncode}): stdout='{result.stdout.strip()}', stderr='{result.stderr.strip()}'" |
| 120 | + |
| 121 | + except subprocess.TimeoutExpired: |
| 122 | + return "AI Error: Timeout - AI processing took too long" |
| 123 | + except Exception as e: |
| 124 | + return f"Subprocess Error: {str(e)}" |
0 commit comments