agyqueue 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
agyqueue/__init__.py ADDED
@@ -0,0 +1 @@
1
+ # AgyQueue package initialization
agyqueue/client.py ADDED
@@ -0,0 +1,129 @@
1
+ import os
2
+ import urllib.request
3
+ import urllib.error
4
+ import urllib.parse
5
+ import json
6
+ import time
7
+ from typing import Optional, Any, List, Dict
8
+
9
+ class AgyQueueClient:
10
+ """Client SDK for interacting with the AgyQueue microservice REST API."""
11
+
12
+ def __init__(self, base_url: Optional[str] = None):
13
+ self.base_url = (base_url or os.environ.get("AGYQUEUE_SERVER_URL", "http://127.0.0.1:8000")).rstrip("/")
14
+
15
+ def _request(self, path: str, method: str = "GET", data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
16
+ url = f"{self.base_url}{path}"
17
+ req_data = None
18
+ headers = {"Content-Type": "application/json"}
19
+
20
+ if data is not None:
21
+ req_data = json.dumps(data).encode("utf-8")
22
+
23
+ req = urllib.request.Request(url, data=req_data, headers=headers, method=method)
24
+ try:
25
+ with urllib.request.urlopen(req, timeout=10.0) as resp:
26
+ return json.loads(resp.read().decode("utf-8"))
27
+ except urllib.error.HTTPError as e:
28
+ try:
29
+ err_body = e.read().decode("utf-8")
30
+ return json.loads(err_body)
31
+ except Exception:
32
+ return {"error": f"HTTP Error {e.code}: {e.reason}"}
33
+ except Exception as e:
34
+ return {"error": f"Connection failed: {str(e)}"}
35
+
36
+ def submit_task(self, prompt: str, task_type: str = "generic") -> Dict[str, Any]:
37
+ """Submit a new task to the AgyQueue service."""
38
+ return self._request("/api/tasks", method="POST", data={"prompt": prompt, "task_type": task_type})
39
+
40
+ def get_task_status(self, task_id: str) -> Dict[str, Any]:
41
+ """Fetch the execution status and progress of a task."""
42
+ return self._request(f"/api/tasks/{task_id}", method="GET")
43
+
44
+ def get_task_result(self, task_id: str) -> Dict[str, Any]:
45
+ """Retrieve the result or error of a completed task."""
46
+ return self._request(f"/api/tasks/{task_id}/result", method="GET")
47
+
48
+ def list_tasks(self) -> List[Dict[str, Any]]:
49
+ """List all tasks in the queue."""
50
+ res = self._request("/api/tasks", method="GET")
51
+ if isinstance(res, list):
52
+ return res
53
+ return []
54
+
55
+ def cancel_task(self, task_id: str) -> Dict[str, Any]:
56
+ """Cancel a running or queued task."""
57
+ return self._request(f"/api/tasks/{task_id}/cancel", method="POST")
58
+
59
+ def wait_for_task(self, task_id: str, poll_interval: float = 2.0, timeout: float = 300.0) -> Dict[str, Any]:
60
+ """Wait for a task to reach a terminal state (COMPLETED, FAILED, CANCELLED)."""
61
+ start_time = time.time()
62
+ while time.time() - start_time < timeout:
63
+ status_res = self.get_task_status(task_id)
64
+ if "error" in status_res:
65
+ return status_res
66
+
67
+ status = status_res.get("status")
68
+ if status in ("COMPLETED", "FAILED", "CANCELLED"):
69
+ return status_res
70
+
71
+ time.sleep(poll_interval)
72
+
73
+ return {"error": "Timeout waiting for task execution completion"}
74
+
75
+
76
+ # AI Agent Tool Wrappers
77
+ # These helper functions match the standard function signature/docstring pattern expected by AI Agent frameworks (like Google ADK, LangChain, etc.).
78
+
79
+ def get_agyqueue_client() -> AgyQueueClient:
80
+ return AgyQueueClient()
81
+
82
+ def submit_async_task(prompt: str, task_type: str = "generic") -> str:
83
+ """Submit a long-running asynchronous task to the background queue.
84
+
85
+ Args:
86
+ prompt: Detailed instructions or code validation prompt.
87
+ task_type: Type of task executor (e.g. 'sre_k8s_analysis', 'fastapi_gen', 'generic').
88
+
89
+ Returns:
90
+ A JSON string containing the submitted task's ID and initial status.
91
+ """
92
+ client = get_agyqueue_client()
93
+ return json.dumps(client.submit_task(prompt, task_type))
94
+
95
+ def check_task_progress(task_id: str) -> str:
96
+ """Check the current status, progress percentage, and step of an active task.
97
+
98
+ Args:
99
+ task_id: The unique task ID returned when the task was submitted.
100
+
101
+ Returns:
102
+ A JSON string detailing current state, progress percentage, and active step.
103
+ """
104
+ client = get_agyqueue_client()
105
+ return json.dumps(client.get_task_status(task_id))
106
+
107
+ def get_task_output(task_id: str) -> str:
108
+ """Retrieve the final completed markdown report or error stack for a task.
109
+
110
+ Args:
111
+ task_id: The unique task ID returned when the task was submitted.
112
+
113
+ Returns:
114
+ A JSON string containing either the completed markdown result or the error payload.
115
+ """
116
+ client = get_agyqueue_client()
117
+ return json.dumps(client.get_task_result(task_id))
118
+
119
+ def cancel_running_task(task_id: str) -> str:
120
+ """Request immediate cancellation and cleanup of a running background task.
121
+
122
+ Args:
123
+ task_id: The unique task ID to cancel.
124
+
125
+ Returns:
126
+ A JSON string confirming cancellation status.
127
+ """
128
+ client = get_agyqueue_client()
129
+ return json.dumps(client.cancel_task(task_id))
agyqueue/config.py ADDED
@@ -0,0 +1,72 @@
1
+ import os
2
+
3
+ class Settings:
4
+ """Consolidated configuration settings for the AgyQueue application."""
5
+
6
+ @property
7
+ def store_type(self) -> str:
8
+ return os.environ.get("AGYQUEUE_STORE_TYPE", "sqlite").lower()
9
+
10
+ @property
11
+ def db_path(self) -> str:
12
+ return os.environ.get("AGYQUEUE_DB_PATH", "agyqueue.db")
13
+
14
+ @property
15
+ def db_host(self) -> Optional[str]:
16
+ return os.environ.get("DB_HOST")
17
+
18
+ @property
19
+ def db_port(self) -> str:
20
+ return os.environ.get("DB_PORT", "5432")
21
+
22
+ @property
23
+ def db_name(self) -> str:
24
+ return os.environ.get("DB_NAME", "agyqueue")
25
+
26
+ @property
27
+ def db_user(self) -> str:
28
+ return os.environ.get("DB_USER", "postgres")
29
+
30
+ @property
31
+ def db_password(self) -> Optional[str]:
32
+ return os.environ.get("DB_PASSWORD")
33
+
34
+ @property
35
+ def database_url(self) -> Optional[str]:
36
+ # Direct URL connection if provided
37
+ url = os.environ.get("DATABASE_URL")
38
+ if url:
39
+ return url
40
+
41
+ # Build URL from components if postgres host is configured
42
+ host = self.db_host
43
+ if host:
44
+ pw = self.db_password
45
+ pw_str = f":{pw}" if pw else ""
46
+ return f"postgresql://{self.db_user}{pw_str}@{host}:{self.db_port}/{self.db_name}"
47
+ return None
48
+
49
+ @property
50
+ def redis_url(self) -> Optional[str]:
51
+ return os.environ.get("REDIS_URL")
52
+
53
+ @property
54
+ def transport(self) -> str:
55
+ return os.environ.get("AGYQUEUE_TRANSPORT", "stdio").lower()
56
+
57
+ @property
58
+ def host(self) -> str:
59
+ return os.environ.get("AGYQUEUE_HOST", "127.0.0.1")
60
+
61
+ @property
62
+ def port(self) -> int:
63
+ return int(os.environ.get("AGYQUEUE_PORT", "8000"))
64
+
65
+ @property
66
+ def heartbeat_timeout(self) -> float:
67
+ return float(os.environ.get("HEARTBEAT_TIMEOUT_SECONDS", "15.0"))
68
+
69
+ from typing import Optional
70
+
71
+ # Global settings instance
72
+ settings = Settings()