Coder preparations

This commit is contained in:
2026-09-22 23:21:59 +03:00
parent 3d1df7fee5
commit 1f2dc25e0b
32 changed files with 3205 additions and 26 deletions
+1
View File
@@ -0,0 +1 @@
class GiteaMCPClient: ...
+4 -1
View File
@@ -6,11 +6,14 @@ 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):
def chat_with_schema(self, prompt: str, schema: dict, system: str | None = None) -> str | None:
assert_strict_mode_clean(schema)
kwargs = {
"model": self.model,
"messages": [
{"role": "system", "content": system},
{"role": "user", "content": prompt},
] if system else [
{"role": "user", "content": prompt},
],
"response_format": schema,
+31
View File
@@ -0,0 +1,31 @@
# orchestrator/time.py
from __future__ import annotations
from datetime import datetime, timedelta, timezone
# Single canonical format for all timestamps the agent writes.
# ISO 8601, UTC, second precision, trailing "Z".
# Examples: "2026-09-21T11:45:00Z"
ISO_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
def now_iso() -> str:
"""Current UTC time as an ISO 8601 string."""
return datetime.now(timezone.utc).strftime(ISO_FORMAT)
def now_iso_plus(
*,
days: int = 0,
hours: int = 0,
minutes: int = 0,
seconds: int = 0,
) -> str:
"""Current UTC time plus a delta, as an ISO 8601 string."""
delta = timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)
return (datetime.now(timezone.utc) + delta).strftime(ISO_FORMAT)
def parse_iso(s: str) -> datetime:
"""Parse an ISO 8601 string produced by now_iso / now_iso_plus."""
return datetime.strptime(s, ISO_FORMAT).replace(tzinfo=timezone.utc)
+49 -1
View File
@@ -1,5 +1,7 @@
import asyncio
import json
import logging
from typing import Optional, Any
import httpx
from contextlib import AsyncExitStack
@@ -109,4 +111,50 @@ class YouTrackMCPClient:
async def list_tools(self):
"""List all available tools from the MCP server."""
return await self._client.list_tools()
return await self._client.list_tools()
@classmethod
def extract_comment_id(cls, result: Any) -> Optional[str]:
"""
Best-effort extraction of the created comment's ID from a
CallToolResult (or any MCP-ish response).
Returns None if the ID can't be determined. Callers MUST tolerate
None by falling back to marker-based lookup on the next Gather.
"""
# 1. Unwrap the MCP envelope.
content = getattr(result, "content", None)
if content is None and isinstance(result, dict):
content = result.get("content")
if not content:
return None
# 2. Take the first text block.
first = content[0]
text = getattr(first, "text", None)
if text is None and isinstance(first, dict):
text = first.get("text")
if not text:
return None
# 3. Try JSON first.
try:
data = json.loads(text or "{}")
except json.JSONDecodeError:
# 4. Not JSON — assume the server returned a bare ID.
return text.strip() or None
# 5. Common shapes: {"id": ...} or {"comment": {"id": ...}}.
if isinstance(data, dict):
if "id" in data:
return str(data["id"])
if "comment" in data and isinstance(data["comment"], dict):
return str(data["comment"].get("id")) or None
# YouTrack often uses "idReadable" for the human-facing ID.
for key in ("idReadable", "commentId", "comment_id"):
if key in data:
return str(data[key])
return None