diff --git a/api/core/llm_generator/llm_generator.py b/api/core/llm_generator/llm_generator.py index 16d1728717..c872ce7118 100644 --- a/api/core/llm_generator/llm_generator.py +++ b/api/core/llm_generator/llm_generator.py @@ -160,14 +160,11 @@ class LLMGenerator: # If input is Persian, ensure candidate contains Persian-specific characters. # Otherwise retry with stronger instruction. if is_persian_input and not _contains_persian(candidate): - logger.info( - "Generated title doesn't appear to be Persian; retrying with stricter instruction" - ) + logger.info("Generated title doesn't appear to be Persian; retrying with stricter instruction") prompts = [ UserPromptMessage( content=( - prompt - + "\nCRITICAL: You must output the title in Persian (Farsi) " + prompt + "\nCRITICAL: You must output the title in Persian (Farsi) " "using Persian-specific letters (پ, چ, ژ, گ, ک, ی). " "Output only the JSON as specified earlier." ) diff --git a/tests/unit_tests/core/llm_generator/test_llm_generator_persian.py b/tests/unit_tests/core/llm_generator/test_llm_generator_persian.py index e3c8a0c4e4..51df6dc46e 100644 --- a/tests/unit_tests/core/llm_generator/test_llm_generator_persian.py +++ b/tests/unit_tests/core/llm_generator/test_llm_generator_persian.py @@ -1,11 +1,14 @@ import sys, types, json from pathlib import Path + # Ensure the repo `api/` directory is importable so tests can import `core.*` without external env setup ROOT = Path(__file__).resolve().parents[3] sys.path.insert(0, str(ROOT / "api")) # Lightweight stubs to avoid importing heavy application modules during unit tests -m = types.ModuleType('core.model_manager') +m = types.ModuleType("core.model_manager") + + class ModelManager: def get_default_model_instance(self, tenant_id, model_type): raise NotImplementedError @@ -13,10 +16,13 @@ class ModelManager: def get_model_instance(self, tenant_id, model_type, provider=None, model=None): raise NotImplementedError -m.ModelManager = ModelManager -sys.modules['core.model_manager'] = m -m2 = types.ModuleType('core.ops.ops_trace_manager') +m.ModelManager = ModelManager +sys.modules["core.model_manager"] = m + +m2 = types.ModuleType("core.ops.ops_trace_manager") + + class TraceTask: def __init__(self, *args, **kwargs): # store attributes for potential inspection in tests @@ -25,53 +31,73 @@ class TraceTask: self.args = args self.kwargs = kwargs + class TraceQueueManager: def __init__(self, *a, **k): pass + def add_trace_task(self, *a, **k): pass + + m2.TraceTask = TraceTask m2.TraceQueueManager = TraceQueueManager -sys.modules['core.ops.ops_trace_manager'] = m2 +sys.modules["core.ops.ops_trace_manager"] = m2 # Stub core.ops.utils to avoid importing heavy dependencies (db, models) during tests -m_ops = types.ModuleType('core.ops.utils') +m_ops = types.ModuleType("core.ops.utils") from contextlib import contextmanager + + @contextmanager def measure_time(): class Timer: pass + t = Timer() yield t -m_ops.measure_time = measure_time -sys.modules['core.ops.utils'] = m_ops -m3 = types.ModuleType('core.model_runtime.entities.llm_entities') + +m_ops.measure_time = measure_time +sys.modules["core.ops.utils"] = m_ops + +m3 = types.ModuleType("core.model_runtime.entities.llm_entities") + + class LLMUsage: @classmethod def empty_usage(cls): return cls() + + class LLMResult: def __init__(self, model=None, prompt_messages=None, message=None, usage=None): self.model = model self.prompt_messages = prompt_messages self.message = message self.usage = usage + + m3.LLMUsage = LLMUsage m3.LLMResult = LLMResult -sys.modules['core.model_runtime.entities.llm_entities'] = m3 +sys.modules["core.model_runtime.entities.llm_entities"] = m3 + +m4 = types.ModuleType("core.model_runtime.entities.message_entities") + -m4 = types.ModuleType('core.model_runtime.entities.message_entities') class PromptMessage: def __init__(self, content=None): self.content = content + def get_text_content(self): return str(self.content) if self.content is not None else "" + class TextPromptMessageContent: def __init__(self, data): self.data = data + class ImagePromptMessageContent: def __init__(self, url=None, base64_data=None, mime_type=None, filename=None): self.url = url @@ -79,28 +105,35 @@ class ImagePromptMessageContent: self.mime_type = mime_type self.filename = filename + class DocumentPromptMessageContent: def __init__(self, url=None): self.url = url + class AudioPromptMessageContent(DocumentPromptMessageContent): pass + class VideoPromptMessageContent(DocumentPromptMessageContent): pass + class AssistantPromptMessage(PromptMessage): def __init__(self, content): super().__init__(content) + class UserPromptMessage(PromptMessage): def __init__(self, content): super().__init__(content) + class SystemPromptMessage(PromptMessage): def __init__(self, content=None): super().__init__(content) + m4.PromptMessage = PromptMessage m4.AssistantPromptMessage = AssistantPromptMessage m4.UserPromptMessage = UserPromptMessage @@ -110,35 +143,55 @@ m4.ImagePromptMessageContent = ImagePromptMessageContent m4.DocumentPromptMessageContent = DocumentPromptMessageContent m4.AudioPromptMessageContent = AudioPromptMessageContent m4.VideoPromptMessageContent = VideoPromptMessageContent -sys.modules['core.model_runtime.entities.message_entities'] = m4 +sys.modules["core.model_runtime.entities.message_entities"] = m4 + +m5 = types.ModuleType("core.model_runtime.entities.model_entities") + -m5 = types.ModuleType('core.model_runtime.entities.model_entities') class ModelType: LLM = None + + m5.ModelType = ModelType -sys.modules['core.model_runtime.entities.model_entities'] = m5 +sys.modules["core.model_runtime.entities.model_entities"] = m5 # Stub minimal 'extensions' and 'models' packages to avoid importing heavy application code during tests -ext_db = types.ModuleType('extensions.ext_database') +ext_db = types.ModuleType("extensions.ext_database") ext_db.db = None -sys.modules['extensions.ext_database'] = ext_db -ext_storage = types.ModuleType('extensions.ext_storage') +sys.modules["extensions.ext_database"] = ext_db +ext_storage = types.ModuleType("extensions.ext_storage") ext_storage.storage = None -sys.modules['extensions.ext_storage'] = ext_storage +sys.modules["extensions.ext_storage"] = ext_storage + +models_m = types.ModuleType("models") + + +class App: + pass + + +class Message: + pass + + +class WorkflowNodeExecutionModel: + pass + -models_m = types.ModuleType('models') -class App: pass -class Message: pass -class WorkflowNodeExecutionModel: pass models_m.App = App models_m.Message = Message models_m.WorkflowNodeExecutionModel = WorkflowNodeExecutionModel -sys.modules['models'] = models_m +sys.modules["models"] = models_m + +models_workflow = types.ModuleType("models.workflow") + + +class Workflow: + pass + -models_workflow = types.ModuleType('models.workflow') -class Workflow: pass models_workflow.Workflow = Workflow -sys.modules['models.workflow'] = models_workflow +sys.modules["models.workflow"] = models_workflow from core.llm_generator.llm_generator import LLMGenerator from core.model_runtime.entities.llm_entities import LLMResult, LLMUsage