From c449a6dfe88ccd10086b6a14aa76fc6798e48115 Mon Sep 17 00:00:00 2001 From: puke <1129090915@qq.com> Date: Sun, 28 Dec 2025 15:06:34 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96LLM=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E5=8C=96=E6=95=B0=E6=8D=AE=E7=9A=84=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pixelle_video/services/llm_service.py | 82 +++++++++++++++------------ 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/pixelle_video/services/llm_service.py b/pixelle_video/services/llm_service.py index 2af6e18..ec48e8a 100644 --- a/pixelle_video/services/llm_service.py +++ b/pixelle_video/services/llm_service.py @@ -215,8 +215,8 @@ class LLMService: """ Call LLM with structured output support - Tries OpenAI beta.chat.completions.parse first, falls back to JSON parsing - if the provider doesn't support structured outputs. + Uses JSON schema instruction appended to prompt for maximum compatibility + across all OpenAI-compatible providers (Qwen, DeepSeek, etc.). Args: client: OpenAI client @@ -230,42 +230,54 @@ class LLMService: Returns: Parsed Pydantic model instance """ - # Try OpenAI structured output API first (beta.chat.completions.parse) - try: - response = await client.beta.chat.completions.parse( - model=model, - messages=[{"role": "user", "content": prompt}], - response_format=response_type, - temperature=temperature, - max_tokens=max_tokens, - **kwargs - ) - - parsed = response.choices[0].message.parsed - if parsed is not None: - logger.debug(f"Structured output parsed successfully via beta API") - return parsed - - # If parsed is None, fall through to fallback - logger.warning("Structured output API returned None, falling back to JSON parsing") - content = response.choices[0].message.content - - except Exception as e: - # If beta API not supported, fall back to JSON mode - logger.debug(f"Structured output API not available ({e}), falling back to JSON parsing") - - response = await client.chat.completions.create( - model=model, - messages=[{"role": "user", "content": prompt}], - temperature=temperature, - max_tokens=max_tokens, - **kwargs - ) - content = response.choices[0].message.content + # Build JSON schema instruction and append to prompt + json_schema_instruction = self._get_json_schema_instruction(response_type) + enhanced_prompt = f"{prompt}\n\n{json_schema_instruction}" - # Fallback: Parse JSON from response content + # Call LLM with enhanced prompt + response = await client.chat.completions.create( + model=model, + messages=[{"role": "user", "content": enhanced_prompt}], + temperature=temperature, + max_tokens=max_tokens, + **kwargs + ) + content = response.choices[0].message.content + + logger.debug(f"Structured output response length: {len(content)} chars") + + # Parse JSON from response content return self._parse_response_as_model(content, response_type) + def _get_json_schema_instruction(self, response_type: Type[T]) -> str: + """ + Generate JSON schema instruction for LLM fallback mode + + Args: + response_type: Pydantic model class + + Returns: + Formatted instruction string with JSON schema + """ + try: + # Get JSON schema from Pydantic model + schema = response_type.model_json_schema() + schema_str = json.dumps(schema, indent=2, ensure_ascii=False) + + return f"""## IMPORTANT: JSON Output Format Required +You MUST respond with ONLY a valid JSON object (no markdown, no extra text). +The JSON must strictly follow this schema: + +```json +{schema_str} +``` + +Output ONLY the JSON object, nothing else.""" + except Exception as e: + logger.warning(f"Failed to generate JSON schema: {e}") + return """## IMPORTANT: JSON Output Format Required +You MUST respond with ONLY a valid JSON object (no markdown, no extra text).""" + def _parse_response_as_model(self, content: str, response_type: Type[T]) -> T: """ Parse LLM response content as Pydantic model