#!/usr/bin/env python3 """ LLM Client för Landvex Produktionsagent Använder Moonshot/Kimi API (OpenAI-kompatibelt) """ import os import json import time from typing import Optional, Dict, Any, List try: from openai import OpenAI except ImportError: raise ImportError("openai paket krävs. Kör: pip install openai") # Konfiguration MOONSHOT_API_KEY = os.environ.get("MOONSHOT_API_KEY") or os.environ.get("KIMI_API_KEY") MOONSHOT_BASE_URL = "https://api.moonshot.cn/v1" DEFAULT_MODEL = "moonshot-v1-128k" # 128k context för långa artiklar class LLMClient: """Klient för att kommunicera med Moonshot/Kimi LLM API.""" def __init__(self, api_key: Optional[str] = None, model: str = DEFAULT_MODEL): self.api_key = api_key or MOONSHOT_API_KEY if not self.api_key: raise ValueError("MOONSHOT_API_KEY eller KIMI_API_KEY måste sättas") self.model = model self.client = OpenAI( api_key=self.api_key, base_url=MOONSHOT_BASE_URL ) def generera_artikel( self, amne: str, doman: str, system_prompt: str, webb_resultat: Optional[List[Dict]] = None, max_tokens: int = 4000, temperatur: float = 0.7 ) -> Dict[str, Any]: """ Generera en komplett artikel om ett infrastrukturobjekt. Args: amne: Objektets namn (t.ex. "Avstängningsventil") doman: Domänkod (t.ex. "VAT") system_prompt: Systemprompt från fil webb_resultat: Resultat från webbsökning (valfritt) max_tokens: Max tokens i svaret temperatur: Kreativitet (0.0-1.0) Returns: Dict med komplett artikeldata """ # Bygg användarprompt med webbdata om tillgängligt webb_kontext = "" if webb_resultat: webb_kontext = "\n\n## Webbresearch:\n" for i, result in enumerate(webb_resultat[:3], 1): webb_kontext += f"\n### Källa {i}: {result.get('title', 'Okänd')}\n" webb_kontext += f"URL: {result.get('url', 'N/A')}\n" webb_kontext += f"Innehåll: {result.get('content', result.get('snippet', ''))[:500]}...\n" user_prompt = f"""Generera en komplett artikel om följande infrastrukturobjekt: Objekt: {amne} Domän: {doman} {webb_kontext} Returnera SVARET SOM JSON i följande format: {{ "slug": "url-vänlig-slug", "namn": {{ "sv": "Svenskt namn", "en": "Engelskt namn" }}, "beskrivning": "Detaljerad beskrivning av objektet, dess funktion och användningsområden (minst 200 ord)", "teknik": {{ "material": ["Material som används"], "dimensioner": "Typiska dimensioner", "standarder": ["Tillämpliga standarder"] }}, "anvandningsomraden": ["Lista över användningsområden"], "relaterade_objekt": ["Lista över relaterade infrastrukturobjekt"], "bilder": [ {{ "beskrivning": "Beskrivning av vad bilden ska visa", "alt_text": "Alt-text för tillgänglighet" }} ], "kallor": [ {{ "titel": "Källans titel", "url": "https://example.com", "typ": "webbsida|standard|dokument" }} ] }} VIKTIGT: Returnera ENDAST JSON, ingen annan text.""" # Anropa LLM response = self.client.chat.completions.create( model=self.model, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt} ], temperature=temperatur, max_tokens=max_tokens, response_format={"type": "json_object"} ) # Parsa JSON-svar try: result = json.loads(response.choices[0].message.content) return result except json.JSONDecodeError as e: raise ValueError(f"Kunde inte parsa LLM-svar som JSON: {e}") def forbattra_artikel( self, befintlig_artikel: Dict[str, Any], instruktioner: str, max_tokens: int = 4000 ) -> Dict[str, Any]: """ Förbättra en befintlig artikel baserat på instruktioner. """ user_prompt = f"""Förbättra följande artikel enligt instruktionerna: ## Instruktioner: {instruktioner} ## Befintlig artikel: {json.dumps(befintlig_artikel, ensure_ascii=False, indent=2)} Returnera den förbättrade artikeln som JSON i samma format.""" response = self.client.chat.completions.create( model=self.model, messages=[ {"role": "user", "content": user_prompt} ], temperature=0.5, max_tokens=max_tokens, response_format={"type": "json_object"} ) return json.loads(response.choices[0].message.content) def testa_llm(): """Testa LLM-anslutning.""" client = LLMClient() print("✓ LLM-klient skapad") # Testa med enkelt anrop response = client.client.chat.completions.create( model=DEFAULT_MODEL, messages=[{"role": "user", "content": "Svara med ett enkelt 'OK'"}], max_tokens=10 ) print(f"✓ LLM svarade: {response.choices[0].message.content}") return True if __name__ == "__main__": testa_llm()