| """Gorev veritabanini guvenli fonksiyonlar olarak modele acar.""" |
|
|
| from __future__ import annotations |
|
|
| from datetime import datetime, time |
| from typing import Any |
| from zoneinfo import ZoneInfo |
|
|
| from pydantic import BaseModel, ConfigDict, Field, ValidationError |
|
|
| from database.database_layer import Database, DatabaseError |
| from core.domain import Priority, TaskCreate, TaskStatus |
|
|
|
|
| class CreateTaskArguments(BaseModel): |
| model_config = ConfigDict(str_strip_whitespace=True) |
|
|
| title: str = Field(min_length=1, max_length=200) |
| deadline: str |
| estimated_minutes: int = Field(ge=15, le=720, multiple_of=15) |
| priority: Priority = Priority.MEDIUM |
|
|
|
|
| class ListTasksArguments(BaseModel): |
| status: TaskStatus | None = None |
| date_from: str | None = None |
| date_to: str | None = None |
|
|
|
|
| class UpdateTaskStatusArguments(BaseModel): |
| task_id: int = Field(gt=0) |
| status: TaskStatus |
|
|
|
|
| class TaskTools: |
| def __init__(self, database: Database, timezone: str = "Europe/Istanbul") -> None: |
| """Tool islemleri icin veritabani ve saat dilimi bagimliliklarini saklar.""" |
|
|
| self.database = database |
| self.timezone = ZoneInfo(timezone) |
|
|
| @property |
| def schemas(self) -> list[dict[str, Any]]: |
| """Modelin cagirabilecegi izinli fonksiyon semalarini dondurur.""" |
|
|
| return [ |
| { |
| "type": "function", |
| "function": { |
| "name": "create_task", |
| "description": ( |
| "Kullanicinin acikca verdigi gorevi veritabanina kaydeder. " |
| "Tahmini sure bilinmiyorsa bu araci cagirma; kullaniciya sor." |
| ), |
| "parameters": { |
| "type": "object", |
| "properties": { |
| "title": {"type": "string"}, |
| "deadline": { |
| "type": "string", |
| "description": "ISO 8601 tarih veya tarih-saat", |
| }, |
| "estimated_minutes": { |
| "type": "integer", |
| "minimum": 15, |
| "maximum": 720, |
| "multipleOf": 15, |
| }, |
| "priority": { |
| "type": "string", |
| "enum": ["low", "medium", "high"], |
| }, |
| }, |
| "required": ["title", "deadline", "estimated_minutes", "priority"], |
| "additionalProperties": False, |
| }, |
| }, |
| }, |
| { |
| "type": "function", |
| "function": { |
| "name": "list_tasks", |
| "description": ( |
| "Oturumdaki gercek gorevleri listeler. Gorevler hakkinda cevap " |
| "vermeden once kullanilmalidir." |
| ), |
| "parameters": { |
| "type": "object", |
| "properties": { |
| "status": { |
| "type": ["string", "null"], |
| "enum": ["active", "completed", None], |
| }, |
| "date_from": {"type": ["string", "null"]}, |
| "date_to": {"type": ["string", "null"]}, |
| }, |
| "additionalProperties": False, |
| }, |
| }, |
| }, |
| { |
| "type": "function", |
| "function": { |
| "name": "update_task_status", |
| "description": ( |
| "Yalniz mevcut bir gorevi aktif veya tamamlandi durumuna getirir. " |
| "Kimlik bilinmiyorsa once list_tasks kullan." |
| ), |
| "parameters": { |
| "type": "object", |
| "properties": { |
| "task_id": {"type": "integer", "minimum": 1}, |
| "status": { |
| "type": "string", |
| "enum": ["active", "completed"], |
| }, |
| }, |
| "required": ["task_id", "status"], |
| "additionalProperties": False, |
| }, |
| }, |
| }, |
| ] |
|
|
| def execute( |
| self, name: str, arguments: dict[str, Any], session_id: str |
| ) -> dict[str, Any]: |
| """Tool adini dogrular, argumanlari parse eder ve ilgili DB islemini calistirir.""" |
|
|
| try: |
| if name == "create_task": |
| parsed = CreateTaskArguments.model_validate(arguments) |
| deadline = self._parse_datetime(parsed.deadline, end_of_day=True) |
| task = self.database.create_task( |
| session_id, |
| TaskCreate( |
| title=parsed.title, |
| deadline=deadline, |
| estimated_minutes=parsed.estimated_minutes, |
| priority=parsed.priority, |
| ), |
| ) |
| return {"ok": True, "task": self._serialize_task(task)} |
| if name == "list_tasks": |
| parsed = ListTasksArguments.model_validate(arguments) |
| tasks = self.database.list_tasks( |
| session_id, |
| parsed.status, |
| self._parse_datetime(parsed.date_from) if parsed.date_from else None, |
| self._parse_datetime(parsed.date_to, end_of_day=True) |
| if parsed.date_to |
| else None, |
| ) |
| return { |
| "ok": True, |
| "count": len(tasks), |
| "tasks": [self._serialize_task(task) for task in tasks], |
| } |
| if name == "update_task_status": |
| parsed = UpdateTaskStatusArguments.model_validate(arguments) |
| task = self.database.update_task_status( |
| session_id, parsed.task_id, parsed.status |
| ) |
| return {"ok": True, "task": self._serialize_task(task)} |
| return {"ok": False, "error": f"Izin verilmeyen arac: {name}"} |
| except (ValidationError, ValueError, DatabaseError) as error: |
| return {"ok": False, "error": str(error)} |
|
|
| def _parse_datetime(self, raw: str, end_of_day: bool = False) -> datetime: |
| """ISO tarih girdisini uygulama saat diliminde timezone-aware degere cevirir.""" |
|
|
| value = raw.strip() |
| try: |
| parsed = datetime.fromisoformat(value) |
| except ValueError as error: |
| raise ValueError("Tarih ISO 8601 biciminde olmalidir") from error |
| if "T" not in value and " " not in value: |
| parsed = datetime.combine( |
| parsed.date(), time(23, 59, 59) if end_of_day else time.min |
| ) |
| if parsed.tzinfo is None: |
| parsed = parsed.replace(tzinfo=self.timezone) |
| return parsed.astimezone(self.timezone) |
|
|
| @staticmethod |
| def _serialize_task(task: Any) -> dict[str, Any]: |
| """Task modelini modele verilecek JSON uyumlu ve oturumsuz veriye cevirir.""" |
|
|
| return task.model_dump(mode="json", exclude={"session_id"}) |
|
|