saksh 1.0.0__tar.gz

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.
@@ -0,0 +1 @@
1
+ recursive-include saksh/dashboard/static *
saksh-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.4
2
+ Name: saksh
3
+ Version: 1.0.0
4
+ Summary: Sahasraksh - The All-Seeing Observer for AI Agents
5
+ Author-email: Ram Bikkina <itsrambikkina@gmail.com>
6
+ Keywords: observability,ai-agents,crewai,langgraph
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: fastapi
13
+ Requires-Dist: uvicorn
14
+ Requires-Dist: requests
15
+ Requires-Dist: pydantic
16
+ Requires-Dist: crewai
17
+ Requires-Dist: build>=1.3.0
18
+ Requires-Dist: twine>=6.0.1
19
+
20
+ # 👁️ Saksh (Sahasraksh) v1.0.0
21
+
22
+ > **"The All-Seeing Observer for your AI Agents."**
23
+
24
+ **Saksh** (derived from *Sahasraksh* — "The One with a Thousand Eyes") is a lightweight, non-intrusive observability layer designed specifically for modern AI Agent ecosystems.
25
+
26
+ Think of it as a **Sidecar** for your agents. whether you're running a swarm of **CrewAI** workers, a complex **LangGraph** state machine, or raw LLM chains, Saksh sits quietly in the background, monitoring health, latency, and vitality without ever blocking your main event loop.
27
+
28
+ ![Saksh Dashboard](./saksh/dashboard/static/image.png)
29
+
30
+
31
+ ## ⚡ Why Saksh?
32
+
33
+ Building agents is hard. Debugging why they "hung" or "timed out" is harder.
34
+ Saksh gives you a **Mission Control** dashboard out of the box, so you can stop guessing if your agents are actually working or just hallucinating silence.
35
+
36
+ * **Universal & Agnostic:** We don't care if you use CrewAI, LangGraph, or raw OpenAI calls. If it has an API key and a URL, we can watch it.
37
+ * **Zero-Touch Dashboard:** Just instantiate the class, and a full React/FastAPI dashboard spins up on port `2604`. No React code required.
38
+ * **Vitality Scoring:** We don't just check "Is it up?". We calculate a **Vitality Score (0-100)** based on latency spikes and auth failures.
39
+ * **Non-Blocking Sidecar:** Runs in a daemon thread. Your agents keep working even if Saksh takes a coffee break.
40
+
41
+ ## 📦 Installation
42
+
43
+ Get it via pip:
44
+
45
+ ```bash
46
+ pip install saksh
47
+
48
+ ```
49
+
50
+ ## 🛠️ Quick Start
51
+
52
+ You can attach Saksh to your existing stack in about 3 lines of code.
53
+
54
+ ### 1. The Setup
55
+
56
+ ```python
57
+ from saksh import Saksh, CrewAINetra, LangGraphNetra
58
+ from crewai import Agent
59
+
60
+ # 1. Summon the Observer 👁️
61
+ # This auto-magically starts the dashboard at http://localhost:2604
62
+ observer = Saksh(start_dashboard=True)
63
+
64
+ ```
65
+
66
+ ### 2. Connect CrewAI
67
+
68
+ Saksh automatically detects the LLM configuration (URL, API Key) inside your CrewAI agents.
69
+
70
+ ```python
71
+ # Your standard CrewAI setup
72
+ researcher = Agent(
73
+ role="Researcher",
74
+ goal="Analyze market trends",
75
+ backstory="You are a data wizard."
76
+ )
77
+
78
+ # Open an Eye on it
79
+ observer.open_eye(CrewAINetra(researcher, "Market-Researcher-01"))
80
+
81
+ ```
82
+
83
+ ### 3. Connect LangGraph
84
+
85
+ Since LangGraph is stateful and abstract, we monitor the underlying LLM provider it relies on.
86
+
87
+ ```python
88
+ # graph_app = workflow.compile()
89
+
90
+ # Register the graph (defaults to checking OpenAI availability)
91
+ observer.open_eye(LangGraphNetra(graph_app, "Math-Graph-State-Machine"))
92
+
93
+ ```
94
+
95
+ *That's it. Your terminal will now log:*
96
+ `👁️ Saksh is watching...`
97
+
98
+ ## 🧩 Architecture
99
+
100
+ Saksh uses a **Netra (Eye) Adapter Pattern**.
101
+
102
+ * **Saksh (Core):** The singleton observer that manages the background thread and dashboard.
103
+ * **Netra (Adapter):** A standardized interface that knows how to "Gaze" at a specific type of agent and normalize its health data into a `HealthPulse`.
104
+
105
+ ## 🤝 Contributing
106
+
107
+ This is **v1.0.0** — the "It Works on My Machine" release.
108
+ We are actively looking for contributors to build **Netras** (Adapters) for:
109
+
110
+ * Autogen
111
+ * Semantic Kernel
112
+ * Local Ollama/Llama.cpp instances
113
+
114
+ PRs are welcome! Let's build the standard for Agent Observability together.
115
+
116
+ ## 📜 License
117
+
118
+ MIT © 2026 Ram Bikkina
saksh-1.0.0/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # 👁️ Saksh (Sahasraksh) v1.0.0
2
+
3
+ > **"The All-Seeing Observer for your AI Agents."**
4
+
5
+ **Saksh** (derived from *Sahasraksh* — "The One with a Thousand Eyes") is a lightweight, non-intrusive observability layer designed specifically for modern AI Agent ecosystems.
6
+
7
+ Think of it as a **Sidecar** for your agents. whether you're running a swarm of **CrewAI** workers, a complex **LangGraph** state machine, or raw LLM chains, Saksh sits quietly in the background, monitoring health, latency, and vitality without ever blocking your main event loop.
8
+
9
+ ![Saksh Dashboard](./saksh/dashboard/static/image.png)
10
+
11
+
12
+ ## ⚡ Why Saksh?
13
+
14
+ Building agents is hard. Debugging why they "hung" or "timed out" is harder.
15
+ Saksh gives you a **Mission Control** dashboard out of the box, so you can stop guessing if your agents are actually working or just hallucinating silence.
16
+
17
+ * **Universal & Agnostic:** We don't care if you use CrewAI, LangGraph, or raw OpenAI calls. If it has an API key and a URL, we can watch it.
18
+ * **Zero-Touch Dashboard:** Just instantiate the class, and a full React/FastAPI dashboard spins up on port `2604`. No React code required.
19
+ * **Vitality Scoring:** We don't just check "Is it up?". We calculate a **Vitality Score (0-100)** based on latency spikes and auth failures.
20
+ * **Non-Blocking Sidecar:** Runs in a daemon thread. Your agents keep working even if Saksh takes a coffee break.
21
+
22
+ ## 📦 Installation
23
+
24
+ Get it via pip:
25
+
26
+ ```bash
27
+ pip install saksh
28
+
29
+ ```
30
+
31
+ ## 🛠️ Quick Start
32
+
33
+ You can attach Saksh to your existing stack in about 3 lines of code.
34
+
35
+ ### 1. The Setup
36
+
37
+ ```python
38
+ from saksh import Saksh, CrewAINetra, LangGraphNetra
39
+ from crewai import Agent
40
+
41
+ # 1. Summon the Observer 👁️
42
+ # This auto-magically starts the dashboard at http://localhost:2604
43
+ observer = Saksh(start_dashboard=True)
44
+
45
+ ```
46
+
47
+ ### 2. Connect CrewAI
48
+
49
+ Saksh automatically detects the LLM configuration (URL, API Key) inside your CrewAI agents.
50
+
51
+ ```python
52
+ # Your standard CrewAI setup
53
+ researcher = Agent(
54
+ role="Researcher",
55
+ goal="Analyze market trends",
56
+ backstory="You are a data wizard."
57
+ )
58
+
59
+ # Open an Eye on it
60
+ observer.open_eye(CrewAINetra(researcher, "Market-Researcher-01"))
61
+
62
+ ```
63
+
64
+ ### 3. Connect LangGraph
65
+
66
+ Since LangGraph is stateful and abstract, we monitor the underlying LLM provider it relies on.
67
+
68
+ ```python
69
+ # graph_app = workflow.compile()
70
+
71
+ # Register the graph (defaults to checking OpenAI availability)
72
+ observer.open_eye(LangGraphNetra(graph_app, "Math-Graph-State-Machine"))
73
+
74
+ ```
75
+
76
+ *That's it. Your terminal will now log:*
77
+ `👁️ Saksh is watching...`
78
+
79
+ ## 🧩 Architecture
80
+
81
+ Saksh uses a **Netra (Eye) Adapter Pattern**.
82
+
83
+ * **Saksh (Core):** The singleton observer that manages the background thread and dashboard.
84
+ * **Netra (Adapter):** A standardized interface that knows how to "Gaze" at a specific type of agent and normalize its health data into a `HealthPulse`.
85
+
86
+ ## 🤝 Contributing
87
+
88
+ This is **v1.0.0** — the "It Works on My Machine" release.
89
+ We are actively looking for contributors to build **Netras** (Adapters) for:
90
+
91
+ * Autogen
92
+ * Semantic Kernel
93
+ * Local Ollama/Llama.cpp instances
94
+
95
+ PRs are welcome! Let's build the standard for Agent Observability together.
96
+
97
+ ## 📜 License
98
+
99
+ MIT © 2026 Ram Bikkina
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "saksh"
7
+ version = "1.0.0"
8
+ description = "Sahasraksh - The All-Seeing Observer for AI Agents"
9
+ readme = "README.md"
10
+ authors = [{name = "Ram Bikkina", email = "itsrambikkina@gmail.com"}]
11
+ dependencies = [
12
+ "fastapi",
13
+ "uvicorn",
14
+ "requests",
15
+ "pydantic",
16
+ "crewai",
17
+ "build>=1.3.0",
18
+ "twine>=6.0.1",
19
+ ]
20
+ requires-python = ">=3.9"
21
+
22
+ # Optional: Add keywords and classifiers to help people find your library
23
+ keywords = ["observability", "ai-agents", "crewai", "langgraph"]
24
+ classifiers = [
25
+ "Programming Language :: Python :: 3",
26
+ "License :: OSI Approved :: MIT License",
27
+ "Operating System :: OS Independent",
28
+ ]
29
+
30
+ [tool.setuptools.package-data]
31
+ saksh = ["dashboard/static/*"]
@@ -0,0 +1,4 @@
1
+ from .core import Saksh, Netra, HealthPulse
2
+ from .adapters import CrewAINetra, LangGraphNetra
3
+
4
+ __all__ = ["Saksh", "Netra", "HealthPulse", "CrewAINetra", "LangGraphNetra"]
@@ -0,0 +1,136 @@
1
+ import requests
2
+ import time
3
+ import os
4
+ from typing import Optional, Dict, Any
5
+ from .core import Netra, HealthPulse
6
+
7
+ # --- SHARED HELPER FUNCTION ---
8
+ def _perform_health_check(agent_id: str, url: str, headers: Dict[str, str], timeout: int = 5) -> HealthPulse:
9
+ """
10
+ Internal helper to ping a URL and return a standardized HealthPulse.
11
+ Used by both CrewAI and LangGraph adapters to avoid code duplication.
12
+ """
13
+ start = time.time()
14
+ status = "down"
15
+ score = 0
16
+
17
+ try:
18
+ # Perform the actual network request
19
+ response = requests.get(url, headers=headers, timeout=timeout)
20
+
21
+ # --- STRICT STATUS CHECK ---
22
+ # 200 = Healthy (Key worked, Server up)
23
+ # 401 = DOWN (Key Failed - Critical Logic)
24
+ # 404 = Degraded (Server up, endpoint might be slightly wrong but alive)
25
+
26
+ if response.status_code == 200:
27
+ status = "healthy"
28
+ score = 100
29
+ elif response.status_code == 401:
30
+ status = "down"
31
+ score = 0
32
+ print(f"⚠️ SAKSH WARNING: Agent {agent_id} Failed Auth (401)")
33
+ else:
34
+ status = "degraded"
35
+ score = 50
36
+
37
+ except requests.exceptions.Timeout:
38
+ status = "degraded" # It's alive but slow
39
+ score = 40
40
+ print(f"⚠️ SAKSH WARNING: Agent {agent_id} Timed Out")
41
+ except Exception:
42
+ status = "down"
43
+ score = 0
44
+
45
+ # Calculate Latency
46
+ latency = (time.time() - start) * 1000
47
+
48
+ # Adjust Score based on Latency (Penalize Slowness)
49
+ if status == "healthy":
50
+ if latency > 1500: score -= 20
51
+ if latency > 3000: score -= 40
52
+
53
+ return HealthPulse(
54
+ agent_id=agent_id,
55
+ status=status,
56
+ latency_ms=round(latency, 2),
57
+ vitality_score=max(0, score) # Ensure score never goes below 0
58
+ )
59
+
60
+
61
+ # --- 1. CREWAI ADAPTER ---
62
+ class CrewAINetra(Netra):
63
+ """
64
+ Specialized for CrewAI Agents.
65
+ Automatically extracts: agent.llm.base_url and agent.llm.api_key
66
+ """
67
+ def __init__(self, crewai_agent, agent_id: str):
68
+ super().__init__(agent_id)
69
+ self.agent = crewai_agent
70
+
71
+ # 1. Logic to extract URL
72
+ # CrewAI often hides the base_url inside the llm object
73
+ if hasattr(crewai_agent.llm, 'base_url') and crewai_agent.llm.base_url:
74
+ self.target_url = crewai_agent.llm.base_url
75
+ self.is_custom = True
76
+ else:
77
+ self.target_url = "https://api.openai.com/v1"
78
+ self.is_custom = False
79
+
80
+ # 2. Logic to extract API Key
81
+ self.api_key = getattr(crewai_agent.llm, 'api_key', None)
82
+
83
+ # Fallback: Check Environment Variable if key is missing in object
84
+ if not self.api_key and not self.is_custom:
85
+ self.api_key = os.getenv("OPENAI_API_KEY")
86
+
87
+ def gaze(self) -> HealthPulse:
88
+ # Construct the Check URL
89
+ if self.is_custom:
90
+ base = self.target_url.rstrip("/")
91
+ if base.endswith("/v1"):
92
+ check_url = f"{base}/models"
93
+ else:
94
+ check_url = f"{base}/v1/models"
95
+ else:
96
+ check_url = "https://api.openai.com/v1/models"
97
+
98
+ # Construct Headers
99
+ headers = {}
100
+ if self.api_key and self.api_key != "na":
101
+ headers["Authorization"] = f"Bearer {self.api_key}"
102
+
103
+ # Delegate to shared helper
104
+ return _perform_health_check(self.agent_id, check_url, headers)
105
+
106
+
107
+ # --- 2. LANGGRAPH ADAPTER ---
108
+ class LangGraphNetra(Netra):
109
+ """
110
+ Specialized for LangGraph Compiled Graphs.
111
+ Since Graphs are abstract, we monitor the underlying LLM provider they rely on.
112
+ """
113
+ def __init__(self, graph_app, agent_id: str, target_url="https://api.openai.com/v1", api_key=None):
114
+ super().__init__(agent_id)
115
+ self.graph = graph_app
116
+
117
+ # LangGraph allows flexible backends, so we accept overrides.
118
+ # Defaults to OpenAI if not specified.
119
+ self.target_url = target_url
120
+ self.api_key = api_key or os.getenv("OPENAI_API_KEY")
121
+
122
+ def gaze(self) -> HealthPulse:
123
+ # Construct URL
124
+ base = self.target_url.rstrip("/")
125
+ if base.endswith("/v1"):
126
+ check_url = f"{base}/models"
127
+ else:
128
+ check_url = f"{base}/v1/models"
129
+
130
+ # Construct Headers
131
+ headers = {}
132
+ if self.api_key:
133
+ headers["Authorization"] = f"Bearer {self.api_key}"
134
+
135
+ # Delegate to shared helper
136
+ return _perform_health_check(self.agent_id, check_url, headers)
@@ -0,0 +1,92 @@
1
+ import threading
2
+ import uvicorn
3
+ import os
4
+ import sys
5
+ from typing import List, Dict, Any, Literal
6
+ from datetime import datetime
7
+ from pydantic import BaseModel, Field
8
+ import abc
9
+ from fastapi import FastAPI
10
+ from fastapi.middleware.cors import CORSMiddleware
11
+ from fastapi.staticfiles import StaticFiles
12
+
13
+ # --- SCHEMAS ---
14
+ class HealthPulse(BaseModel):
15
+ agent_id: str
16
+ status: Literal["healthy", "degraded", "down"]
17
+ latency_ms: float
18
+ timestamp: datetime = Field(default_factory=datetime.now)
19
+ vitality_score: int
20
+
21
+ class Netra(abc.ABC):
22
+ def __init__(self, agent_id: str):
23
+ self.agent_id = agent_id
24
+
25
+ @abc.abstractmethod
26
+ def gaze(self) -> HealthPulse:
27
+ pass
28
+
29
+ class Saksh:
30
+ _instance = None
31
+
32
+ def __new__(cls, *args, **kwargs):
33
+ if not cls._instance:
34
+ cls._instance = super(Saksh, cls).__new__(cls)
35
+ return cls._instance
36
+
37
+ def __init__(self, start_dashboard=True, port=2604):
38
+ if hasattr(self, '_initialized'):
39
+ return
40
+
41
+ self._thousand_eyes: List[Netra] = []
42
+ self._initialized = True
43
+
44
+ if start_dashboard:
45
+ self._start_dashboard_thread(port)
46
+
47
+ def open_eye(self, adapter: Netra):
48
+ self._thousand_eyes.append(adapter)
49
+
50
+ def observe(self) -> Dict[str, Any]:
51
+ report = {"total": len(self._thousand_eyes), "status": "stable", "eyes": []}
52
+ for eye in self._thousand_eyes:
53
+ pulse = eye.gaze()
54
+ report["eyes"].append(pulse.dict())
55
+ if pulse.status == "down":
56
+ report["status"] = "critical"
57
+ return report
58
+
59
+ def _start_dashboard_thread(self, port):
60
+ def run_server():
61
+ app = FastAPI(title="Saksh Sidecar")
62
+
63
+ app.add_middleware(
64
+ CORSMiddleware,
65
+ allow_origins=["*"],
66
+ allow_methods=["*"],
67
+ allow_headers=["*"],
68
+ )
69
+
70
+ @app.get("/api/status")
71
+ def get_status():
72
+ return self.observe()
73
+
74
+ # --- PATH FIX FOR LIBRARY ---
75
+ # Finds the 'dashboard/static' folder relative to THIS file (core.py)
76
+ current_dir = os.path.dirname(os.path.abspath(__file__))
77
+ static_dir = os.path.join(current_dir, "dashboard", "static")
78
+
79
+ if os.path.exists(static_dir):
80
+ app.mount("/", StaticFiles(directory=static_dir, html=True), name="static")
81
+ else:
82
+ print(f"⚠️ SAKSH WARNING: Static files not found at {static_dir}")
83
+
84
+ print(f"\n👁️ SAKSH DASHBOARD ONLINE: http://localhost:{port}\n")
85
+
86
+ config = uvicorn.Config(app, host="0.0.0.0", port=port, log_level="error", loop="asyncio")
87
+ server = uvicorn.Server(config)
88
+ server.install_signal_handlers = lambda: None
89
+ server.run()
90
+
91
+ thread = threading.Thread(target=run_server, daemon=True)
92
+ thread.start()
@@ -0,0 +1,48 @@
1
+ from fastapi import FastAPI
2
+ from fastapi.staticfiles import StaticFiles
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ import uvicorn
5
+ import sys
6
+ import os
7
+
8
+ # Add root directory to sys.path so we can import 'core' and 'agents'
9
+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
10
+
11
+ from core.saksh import Saksh
12
+ from core.adapters import CrewAINetra
13
+ from agents.qwen_agent import get_qwen_agent
14
+ from agents.gpt_agent import get_gpt_agent
15
+
16
+ app = FastAPI(title="Saksh Observability")
17
+
18
+ # Enable CORS (Allows local development flexibility)
19
+ app.add_middleware(
20
+ CORSMiddleware,
21
+ allow_origins=["*"],
22
+ allow_methods=["*"],
23
+ allow_headers=["*"],
24
+ )
25
+
26
+ # --- INITIALIZE SAKSH ---
27
+ # We instantiate the observer here. It acts as a standalone monitor.
28
+ observer = Saksh()
29
+ qwen = get_qwen_agent()
30
+ gpt = get_gpt_agent()
31
+
32
+ # Register Eyes
33
+ observer.open_eye(CrewAINetra(qwen, "Qwen-Custom-01"))
34
+ observer.open_eye(CrewAINetra(gpt, "OpenAI-GPT4-01"))
35
+
36
+ # --- API ENDPOINTS ---
37
+
38
+ @app.get("/api/status")
39
+ def get_status():
40
+ """Returns the JSON health matrix for the UI to consume"""
41
+ return observer.observe()
42
+
43
+ # --- SERVE STATIC FILES (HTML/JS/CSS) ---
44
+ app.mount("/", StaticFiles(directory="dashboard/static", html=True), name="static")
45
+
46
+ if __name__ == "__main__":
47
+ print("👁️ SAKSH Dashboard running at http://localhost:2604")
48
+ uvicorn.run(app, host="0.0.0.0", port=2604)
@@ -0,0 +1,61 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Saksh | Mission Control</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
8
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
9
+ <link rel="stylesheet" href="style.css">
10
+ </head>
11
+ <body class="bg-black text-light">
12
+
13
+ <nav class="navbar navbar-dark border-bottom border-secondary p-3 mb-4" style="background-color: #0d1117;">
14
+ <div class="container">
15
+ <a class="navbar-brand fw-bold d-flex align-items-center" href="#">
16
+ <span class="pulse-dot me-3"></span>
17
+ 👁️ SAKSH <span class="badge bg-secondary ms-2 small-badge">OBSERVER</span>
18
+ </a>
19
+ <span class="navbar-text text-success" id="system-status" style="font-size: 0.9rem;">
20
+ ● System Online
21
+ </span>
22
+ </div>
23
+ </nav>
24
+
25
+ <div class="container">
26
+
27
+ <div class="row mb-5 text-center">
28
+ <div class="col-md-4">
29
+ <div class="stat-box">
30
+ <small class="text-uppercase text-muted fw-bold" style="letter-spacing: 1px;">Active Agents</small>
31
+ <h1 id="total-agents" class="display-4 fw-bold text-white mt-2">--</h1>
32
+ </div>
33
+ </div>
34
+ <div class="col-md-4">
35
+ <div class="stat-box">
36
+ <small class="text-uppercase text-muted fw-bold" style="letter-spacing: 1px;">Ecosystem Status</small>
37
+ <h1 id="overall-health" class="display-4 fw-bold mt-2">--</h1>
38
+ </div>
39
+ </div>
40
+ <div class="col-md-4">
41
+ <div class="stat-box">
42
+ <small class="text-uppercase text-muted fw-bold" style="letter-spacing: 1px;">Last Heartbeat</small>
43
+ <h1 id="last-update" class="display-6 mt-3 text-white">--:--:--</h1>
44
+ </div>
45
+ </div>
46
+ </div>
47
+
48
+ <div class="d-flex justify-content-between align-items-center mb-3 border-bottom border-secondary pb-2">
49
+ <h5 class="text-muted m-0 text-uppercase">Live Agent Telemetry</h5>
50
+ <span class="badge bg-dark border border-secondary text-muted">AUTO-REFRESH: 2s</span>
51
+ </div>
52
+
53
+ <div class="row g-4" id="agent-grid">
54
+ </div>
55
+
56
+ </div>
57
+
58
+ <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
59
+ <script src="script.js"></script>
60
+ </body>
61
+ </html>
@@ -0,0 +1,68 @@
1
+ $(document).ready(function() {
2
+
3
+ function fetchPulse() {
4
+ $.ajax({
5
+ url: '/api/status',
6
+ method: 'GET',
7
+ success: function(data) {
8
+ updateDashboard(data);
9
+ $('#system-status').html('<span class="text-success">● System Online</span>');
10
+ },
11
+ error: function(err) {
12
+ $('#system-status').html('<span class="text-danger">⚠️ Connection Lost</span>');
13
+ $('.pulse-dot').css('background-color', 'red');
14
+ }
15
+ });
16
+ }
17
+
18
+ function updateDashboard(data) {
19
+ // 1. Update Header Stats
20
+ $('#total-agents').text(data.total);
21
+
22
+ let healthColor = data.status === "stable" ? "text-healthy" : "text-down";
23
+ $('#overall-health').html(`<span class="${healthColor}">${data.status.toUpperCase()}</span>`);
24
+
25
+ let now = new Date();
26
+ $('#last-update').text(now.toLocaleTimeString());
27
+
28
+ // 2. Generate Grid HTML
29
+ let gridHtml = '';
30
+ data.eyes.forEach(agent => {
31
+ let statusColor = agent.status === 'healthy' ? 'text-healthy' :
32
+ agent.status === 'degraded' ? 'text-degraded' : 'text-down';
33
+
34
+ let borderColor = `border-${agent.status}`; // references css class
35
+
36
+ let icon = agent.status === 'healthy' ? '⚡' : '⚠️';
37
+ if(agent.status === 'down') icon = '🛑';
38
+
39
+ gridHtml += `
40
+ <div class="col-md-4">
41
+ <div class="agent-card ${borderColor} p-4 h-100">
42
+ <div class="d-flex justify-content-between align-items-start mb-3">
43
+ <h5 class="fw-bold mb-0 text-white">${icon} ${agent.agent_id}</h5>
44
+ <span class="badge bg-dark border border-secondary">${agent.latency_ms} ms</span>
45
+ </div>
46
+
47
+ <div class="text-center py-2">
48
+ <div class="vitality-number ${statusColor}">${agent.vitality_score}</div>
49
+ <small class="text-secondary text-uppercase tracking-wider">Vitality Score</small>
50
+ </div>
51
+
52
+ <div class="mt-3 pt-3 border-top border-secondary d-flex justify-content-between">
53
+ <small class="text-muted">STATUS</small>
54
+ <small class="fw-bold ${statusColor} text-uppercase">${agent.status}</small>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ `;
59
+ });
60
+
61
+ // 3. Update DOM (Only if changed to prevent flicker, simplified here to direct replace)
62
+ $('#agent-grid').html(gridHtml);
63
+ }
64
+
65
+ // Start Polling
66
+ fetchPulse();
67
+ setInterval(fetchPulse, 2000);
68
+ });
@@ -0,0 +1,77 @@
1
+ /* style.css */
2
+ body {
3
+ font-family: 'JetBrains Mono', monospace;
4
+ background-color: #050505 !important;
5
+ color: #e0e0e0;
6
+ }
7
+
8
+ /* --- Text Visibility Fixes --- */
9
+ /* Overriding Bootstrap's text-muted to be visible on black */
10
+ .text-muted {
11
+ color: #8b949e !important; /* Lighter Gray */
12
+ }
13
+
14
+ h5.text-muted {
15
+ color: #8b949e !important;
16
+ font-weight: 600;
17
+ letter-spacing: 1px;
18
+ }
19
+ /* ----------------------------- */
20
+
21
+ /* Navbar Pulse Dot */
22
+ .pulse-dot {
23
+ width: 10px;
24
+ height: 10px;
25
+ background-color: #00ff00;
26
+ border-radius: 50%;
27
+ box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.7);
28
+ animation: pulse-green 2s infinite;
29
+ }
30
+
31
+ @keyframes pulse-green {
32
+ 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.7); }
33
+ 70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(0, 255, 0, 0); }
34
+ 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(0, 255, 0, 0); }
35
+ }
36
+
37
+ /* Stat Boxes */
38
+ .stat-box {
39
+ background: #0d1117;
40
+ border: 1px solid #30363d;
41
+ border-radius: 6px;
42
+ padding: 20px;
43
+ height: 100%; /* Ensure equal height */
44
+ }
45
+
46
+ /* Agent Cards */
47
+ .agent-card {
48
+ background: #0d1117;
49
+ border: 1px solid #30363d;
50
+ border-radius: 8px;
51
+ transition: all 0.3s ease;
52
+ }
53
+
54
+ .agent-card:hover {
55
+ transform: translateY(-2px);
56
+ box-shadow: 0 4px 15px rgba(0,0,0,0.5);
57
+ }
58
+
59
+ /* Dynamic Status Borders */
60
+ .border-healthy { border-top: 4px solid #2ea043 !important; }
61
+ .border-degraded { border-top: 4px solid #d29922 !important; }
62
+ .border-down { border-top: 4px solid #da3633 !important; }
63
+
64
+ /* Text Colors */
65
+ .text-healthy { color: #2ea043; }
66
+ .text-degraded { color: #d29922; }
67
+ .text-down { color: #da3633; }
68
+
69
+ .vitality-number {
70
+ font-size: 3.5rem; /* Slightly larger */
71
+ font-weight: 700;
72
+ }
73
+
74
+ .small-badge {
75
+ font-size: 0.6em;
76
+ vertical-align: middle;
77
+ }
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.4
2
+ Name: saksh
3
+ Version: 1.0.0
4
+ Summary: Sahasraksh - The All-Seeing Observer for AI Agents
5
+ Author-email: Ram Bikkina <itsrambikkina@gmail.com>
6
+ Keywords: observability,ai-agents,crewai,langgraph
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: fastapi
13
+ Requires-Dist: uvicorn
14
+ Requires-Dist: requests
15
+ Requires-Dist: pydantic
16
+ Requires-Dist: crewai
17
+ Requires-Dist: build>=1.3.0
18
+ Requires-Dist: twine>=6.0.1
19
+
20
+ # 👁️ Saksh (Sahasraksh) v1.0.0
21
+
22
+ > **"The All-Seeing Observer for your AI Agents."**
23
+
24
+ **Saksh** (derived from *Sahasraksh* — "The One with a Thousand Eyes") is a lightweight, non-intrusive observability layer designed specifically for modern AI Agent ecosystems.
25
+
26
+ Think of it as a **Sidecar** for your agents. whether you're running a swarm of **CrewAI** workers, a complex **LangGraph** state machine, or raw LLM chains, Saksh sits quietly in the background, monitoring health, latency, and vitality without ever blocking your main event loop.
27
+
28
+ ![Saksh Dashboard](./saksh/dashboard/static/image.png)
29
+
30
+
31
+ ## ⚡ Why Saksh?
32
+
33
+ Building agents is hard. Debugging why they "hung" or "timed out" is harder.
34
+ Saksh gives you a **Mission Control** dashboard out of the box, so you can stop guessing if your agents are actually working or just hallucinating silence.
35
+
36
+ * **Universal & Agnostic:** We don't care if you use CrewAI, LangGraph, or raw OpenAI calls. If it has an API key and a URL, we can watch it.
37
+ * **Zero-Touch Dashboard:** Just instantiate the class, and a full React/FastAPI dashboard spins up on port `2604`. No React code required.
38
+ * **Vitality Scoring:** We don't just check "Is it up?". We calculate a **Vitality Score (0-100)** based on latency spikes and auth failures.
39
+ * **Non-Blocking Sidecar:** Runs in a daemon thread. Your agents keep working even if Saksh takes a coffee break.
40
+
41
+ ## 📦 Installation
42
+
43
+ Get it via pip:
44
+
45
+ ```bash
46
+ pip install saksh
47
+
48
+ ```
49
+
50
+ ## 🛠️ Quick Start
51
+
52
+ You can attach Saksh to your existing stack in about 3 lines of code.
53
+
54
+ ### 1. The Setup
55
+
56
+ ```python
57
+ from saksh import Saksh, CrewAINetra, LangGraphNetra
58
+ from crewai import Agent
59
+
60
+ # 1. Summon the Observer 👁️
61
+ # This auto-magically starts the dashboard at http://localhost:2604
62
+ observer = Saksh(start_dashboard=True)
63
+
64
+ ```
65
+
66
+ ### 2. Connect CrewAI
67
+
68
+ Saksh automatically detects the LLM configuration (URL, API Key) inside your CrewAI agents.
69
+
70
+ ```python
71
+ # Your standard CrewAI setup
72
+ researcher = Agent(
73
+ role="Researcher",
74
+ goal="Analyze market trends",
75
+ backstory="You are a data wizard."
76
+ )
77
+
78
+ # Open an Eye on it
79
+ observer.open_eye(CrewAINetra(researcher, "Market-Researcher-01"))
80
+
81
+ ```
82
+
83
+ ### 3. Connect LangGraph
84
+
85
+ Since LangGraph is stateful and abstract, we monitor the underlying LLM provider it relies on.
86
+
87
+ ```python
88
+ # graph_app = workflow.compile()
89
+
90
+ # Register the graph (defaults to checking OpenAI availability)
91
+ observer.open_eye(LangGraphNetra(graph_app, "Math-Graph-State-Machine"))
92
+
93
+ ```
94
+
95
+ *That's it. Your terminal will now log:*
96
+ `👁️ Saksh is watching...`
97
+
98
+ ## 🧩 Architecture
99
+
100
+ Saksh uses a **Netra (Eye) Adapter Pattern**.
101
+
102
+ * **Saksh (Core):** The singleton observer that manages the background thread and dashboard.
103
+ * **Netra (Adapter):** A standardized interface that knows how to "Gaze" at a specific type of agent and normalize its health data into a `HealthPulse`.
104
+
105
+ ## 🤝 Contributing
106
+
107
+ This is **v1.0.0** — the "It Works on My Machine" release.
108
+ We are actively looking for contributors to build **Netras** (Adapters) for:
109
+
110
+ * Autogen
111
+ * Semantic Kernel
112
+ * Local Ollama/Llama.cpp instances
113
+
114
+ PRs are welcome! Let's build the standard for Agent Observability together.
115
+
116
+ ## 📜 License
117
+
118
+ MIT © 2026 Ram Bikkina
@@ -0,0 +1,16 @@
1
+ MANIFEST.in
2
+ README.md
3
+ pyproject.toml
4
+ saksh/__init__.py
5
+ saksh/adapters.py
6
+ saksh/core.py
7
+ saksh.egg-info/PKG-INFO
8
+ saksh.egg-info/SOURCES.txt
9
+ saksh.egg-info/dependency_links.txt
10
+ saksh.egg-info/requires.txt
11
+ saksh.egg-info/top_level.txt
12
+ saksh/dashboard/server.py
13
+ saksh/dashboard/static/image.png
14
+ saksh/dashboard/static/index.html
15
+ saksh/dashboard/static/script.js
16
+ saksh/dashboard/static/style.css
@@ -0,0 +1,7 @@
1
+ fastapi
2
+ uvicorn
3
+ requests
4
+ pydantic
5
+ crewai
6
+ build>=1.3.0
7
+ twine>=6.0.1
@@ -0,0 +1 @@
1
+ saksh
saksh-1.0.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+