import json import os import urllib.error import urllib.request from typing import Dict def structure_to_json(text: str, schema: Dict) -> Dict: """ Convert structured text to JSON using LOCAL_LLM_API. Args: text (str): The structured text to convert schema (dict): The JSON schema to use for conversion Returns: dict: The parsed JSON object """ # Get the API URL from environment variable local_llm_api = os.environ.get( "LOCAL_LLM_API", "http://host.docker.internal:1248/v1" ) # Format the schema as JSON string schema_json = json.dumps(schema, indent=2) # Create the prompt following the pattern from jj-ai-commit.sh prompt_text = f"""Analyse this text and respond ONLY with a valid JSON object matching the schema. Text: {text} Schema: {schema_json}""" # Build the request body request_body = { "model": "qwen3-coder", "messages": [{"role": "user", "content": prompt_text}], } # Build the URL url = f"{local_llm_api}/chat/completions" # Create the request data = json.dumps(request_body).encode("utf-8") req = urllib.request.Request( url, data=data, headers={"Content-Type": "application/json"}, method="POST" ) try: # Execute the request with urllib.request.urlopen(req) as response: result = json.loads(response.read().decode("utf-8")) except urllib.error.URLError as e: return {"error": f"API request failed: {e}", "raw_response": ""} except json.JSONDecodeError as e: return {"error": f"Failed to parse response: {e}", "raw_response": ""} try: content = result.get("choices", [{}])[0].get("message", {}).get("content", "") # Clean markdown backticks from response clean_content = content.strip() while clean_content.startswith("`") or clean_content.startswith("```"): clean_content = clean_content[1:].strip() while clean_content.endswith("`") or clean_content.endswith("```"): clean_content = clean_content[:-1].strip() # Parse the JSON content return json.loads(clean_content) except (json.JSONDecodeError, KeyError, IndexError) as e: return { "error": f"Failed to parse JSON: {e}", "raw_response": json.dumps(result), }