2828
2929
3030# Import unified operation system
31- from api_decorators import operation
3231from mcp import handle_mcp_request
33- from ollama_service import ChatRequest , ChatResponse , ModelListResponse , OllamaService
32+ from ollama_service import ChatRequest
33+ from operations import (op_create_task , op_delete_task , op_get_task ,
34+ op_get_task_stats , op_list_ollama_models ,
35+ op_list_tasks , op_ollama_chat , op_update_task ,
36+ task_service )
3437from pydantic import ValidationError
3538from quart import Quart , jsonify , request , send_from_directory
3639from quart_cors import cors
37-
3840# Import Pydantic models and service
39- from tasks import Task , TaskCreate , TaskFilter , TaskService , TaskStats , TaskUpdate
41+ from tasks import Task , TaskCreate , TaskFilter , TaskStats , TaskUpdate
4042
4143# ============================================================================
4244# APPLICATION SETUP
4547app = Quart (__name__ )
4648app = cors (app , allow_origin = "*" )
4749
48- # Service instances
49- task_service = TaskService ()
50- ollama_service = OllamaService ()
50+ # Service instances live in operations.py so every interface shares them
5151
5252
5353# ============================================================================
@@ -59,144 +59,10 @@ def format_datetime(dt: datetime) -> str:
5959 return dt .isoformat ()
6060
6161
62- # ============================================================================
62+ # =========================================================================
6363# UNIFIED OPERATIONS
64- # Define once with Pydantic types, use for both REST and MCP!
65- # ============================================================================
66-
67- @operation (
68- name = "list_tasks" ,
69- description = "List all tasks with optional filtering by completion status" ,
70- http_method = "GET" ,
71- http_path = "/api/tasks"
72- )
73- async def op_list_tasks (filter : TaskFilter = TaskFilter .ALL ) -> list [Task ]:
74- """
75- List tasks with optional filtering.
76-
77- This operation serves BOTH:
78- - REST API: GET /api/tasks?filter=...
79- - MCP tool: list_tasks(filter=...)
80-
81- Pydantic's TaskFilter enum automatically generates MCP schema!
82- """
83- return task_service .list_tasks (filter )
84-
85-
86- @operation (
87- name = "create_task" ,
88- description = "Create a new task with validation" ,
89- http_method = "POST" ,
90- http_path = "/api/tasks"
91- )
92- async def op_create_task (data : TaskCreate ) -> Task :
93- """
94- Create a new task.
95-
96- Pydantic automatically:
97- - Validates title is not empty
98- - Trims whitespace
99- - Generates JSON schema for MCP
100-
101- Returns the created Task model.
102- """
103- return task_service .create_task (data )
104-
105-
106- @operation (
107- name = "get_task" ,
108- description = "Retrieve a specific task by its unique identifier" ,
109- http_method = "GET" ,
110- http_path = "/api/tasks/{task_id}"
111- )
112- async def op_get_task (task_id : str ) -> Task | None :
113- """
114- Get task by ID.
115-
116- Returns None if not found - caller handles 404.
117- """
118- return task_service .get_task (task_id )
119-
120-
121- @operation (
122- name = "update_task" ,
123- description = "Update an existing task's properties" ,
124- http_method = "PUT" ,
125- http_path = "/api/tasks/{task_id}"
126- )
127- async def op_update_task (task_id : str , data : TaskUpdate ) -> Task | None :
128- """
129- Update task with validation.
130-
131- TaskUpdate has all optional fields - only provided fields are updated.
132- Pydantic validates any provided values automatically.
133-
134- Returns None if task not found.
135- """
136- return task_service .update_task (task_id , data )
137-
138-
139- @operation (
140- name = "delete_task" ,
141- description = "Delete a task permanently by its identifier" ,
142- http_method = "DELETE" ,
143- http_path = "/api/tasks/{task_id}"
144- )
145- async def op_delete_task (task_id : str ) -> bool :
146- """
147- Delete task.
148-
149- Returns True if deleted, False if not found.
150- """
151- return task_service .delete_task (task_id )
152-
153-
154- @operation (
155- name = "get_task_stats" ,
156- description = "Get summary statistics for all tasks" ,
157- http_method = "GET" ,
158- http_path = "/api/tasks/stats"
159- )
160- async def op_get_task_stats () -> TaskStats :
161- """
162- Get task statistics.
163-
164- Returns Pydantic TaskStats model with total, completed, pending counts.
165- """
166- return task_service .get_stats ()
167-
168-
169- @operation (
170- name = "ollama_chat" ,
171- description = "Generate a chat completion using local Ollama LLM" ,
172- http_method = "POST" ,
173- http_path = "/api/ollama/chat"
174- )
175- async def op_ollama_chat (request : ChatRequest ) -> ChatResponse :
176- """
177- Chat with Ollama LLM.
178-
179- Supports conversation history and configurable temperature.
180- Pydantic automatically validates message format and parameters.
181-
182- Returns the generated response with metadata.
183- """
184- return await ollama_service .chat (request )
185-
186-
187- @operation (
188- name = "list_ollama_models" ,
189- description = "List all available Ollama models on the local system" ,
190- http_method = "GET" ,
191- http_path = "/api/ollama/models"
192- )
193- async def op_list_ollama_models () -> ModelListResponse :
194- """
195- List available Ollama models.
196-
197- Returns list of models with name, size, and modification time.
198- """
199- return await ollama_service .list_models ()
64+ # Defined once in operations.py so REST, MCP, and agents share logic.
65+ # =========================================================================
20066
20167
20268# ============================================================================
@@ -301,6 +167,84 @@ async def rest_list_ollama_models():
301167
302168
303169
170+ # ============================================================================
171+ # QA TICKETS ENDPOINT
172+ # ============================================================================
173+
174+ @app .route ("/api/qa-tickets" , methods = ["GET" ])
175+ async def get_qa_tickets ():
176+ """Get QA tickets that need escalation."""
177+ # Mock data - will be replaced with real database later
178+ tickets = [
179+ {
180+ "id" : "INC-1001" ,
181+ "title" : "Login page nicht erreichbar" ,
182+ "status" : "Open" ,
183+ "priority" : "High" ,
184+ "assignee" : None ,
185+ "createdAt" : "2025-12-09T08:30:00Z" ,
186+ "updatedAt" : "2025-12-09T08:30:00Z" ,
187+ "escalationNeeded" : True ,
188+ "description" : "Benutzer können sich nicht anmelden. Die Login-Seite gibt einen 500-Fehler zurück. Betrifft alle Umgebungen." ,
189+ "category" : "QA" ,
190+ "reporter" : "M. Schmidt" ,
191+ },
192+ {
193+ "id" : "INC-1002" ,
194+ "title" : "Performance-Problem im Dashboard" ,
195+ "status" : "Open" ,
196+ "priority" : "Medium" ,
197+ "assignee" : None ,
198+ "createdAt" : "2025-12-09T10:15:00Z" ,
199+ "updatedAt" : "2025-12-09T10:15:00Z" ,
200+ "escalationNeeded" : True ,
201+ "description" : "Dashboard lädt sehr langsam (>10 Sekunden). API-Aufrufe scheinen blockiert zu sein." ,
202+ "category" : "QA" ,
203+ "reporter" : "A. Müller" ,
204+ },
205+ {
206+ "id" : "INC-1003" ,
207+ "title" : "Datenverlust beim Speichern" ,
208+ "status" : "Open" ,
209+ "priority" : "Critical" ,
210+ "assignee" : None ,
211+ "createdAt" : "2025-12-09T14:45:00Z" ,
212+ "updatedAt" : "2025-12-09T14:45:00Z" ,
213+ "escalationNeeded" : True ,
214+ "description" : "Formulardaten werden nicht gespeichert. Validierung schlägt fehl ohne Fehlermeldung." ,
215+ "category" : "QA" ,
216+ "reporter" : "T. Weber" ,
217+ },
218+ {
219+ "id" : "INC-1004" ,
220+ "title" : "Export-Funktion fehlerhaft" ,
221+ "status" : "Open" ,
222+ "priority" : "Low" ,
223+ "assignee" : None ,
224+ "createdAt" : "2025-12-10T09:00:00Z" ,
225+ "updatedAt" : "2025-12-10T09:00:00Z" ,
226+ "escalationNeeded" : True ,
227+ "description" : "CSV-Export generiert leere Dateien. Excel-Export funktioniert korrekt." ,
228+ "category" : "QA" ,
229+ "reporter" : "L. Fischer" ,
230+ },
231+ {
232+ "id" : "INC-1005" ,
233+ "title" : "Notification-Service offline" ,
234+ "status" : "Open" ,
235+ "priority" : "High" ,
236+ "assignee" : None ,
237+ "createdAt" : "2025-12-10T11:30:00Z" ,
238+ "updatedAt" : "2025-12-10T11:30:00Z" ,
239+ "escalationNeeded" : True ,
240+ "description" : "E-Mail-Benachrichtigungen werden nicht versendet. Queue läuft voll." ,
241+ "category" : "QA" ,
242+ "reporter" : "K. Becker" ,
243+ },
244+ ]
245+ return jsonify ({"tickets" : tickets })
246+
247+
304248# ============================================================================
305249# NON-TASK ENDPOINTS
306250# ============================================================================
0 commit comments