IssueTriage can now interact with task in YouTrack and it is idempotent

This commit is contained in:
2026-09-20 20:56:36 +03:00
parent 74577cb50e
commit f25d9a5fab
34 changed files with 1834 additions and 201 deletions
+41 -1
View File
@@ -6,6 +6,18 @@ class LLMClient:
self.model = model
self.client = OpenAI(base_url=base_url, api_key=api_key or "ollama")
def chat_with_schema(self, prompt: str, schema: dict):
assert_strict_mode_clean(schema)
kwargs = {
"model": self.model,
"messages": [
{"role": "user", "content": prompt},
],
"response_format": schema,
}
resp = self.client.chat.completions.create(**kwargs)
return resp.choices[0].message.content
def chat(self, system: str, user: str, json_mode: bool = False, images: list[str] | None = None) -> str | None:
images = images or []
if images:
@@ -28,4 +40,32 @@ class LLMClient:
if json_mode:
kwargs["response_format"] = {"type": "json_object"}
resp = self.client.chat.completions.create(**kwargs)
return resp.choices[0].message.content
return resp.choices[0].message.content
def assert_strict_mode_clean(schema: dict, path: str = "$") -> None:
"""Recursively verify a JSON Schema obeys OpenAI strict mode."""
if schema.get("type") == "object":
props = schema.get("properties", {})
required = set(schema.get("required", []))
if schema.get("additionalProperties") is not False:
raise ValueError(f"{path}: additionalProperties must be false")
for name in props:
if name not in required:
raise ValueError(f"{path}.{name}: not in required")
for name, sub in props.items():
assert_strict_mode_clean(sub, f"{path}.{name}")
if schema.get("type") == "array":
assert_strict_mode_clean(schema["items"], f"{path}[]")
for name, sub in schema.get("$defs", {}).items():
assert_strict_mode_clean(sub, f"$defs.{name}")
# OpenAI rejects these keywords in strict mode
for banned in ("default", "oneOf"):
if banned in schema:
raise ValueError(f"{path}: '{banned}' not allowed in strict mode")