cortexops 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.
- cortexops/LICENSE +21 -0
- cortexops/README.md +106 -0
- cortexops/__init__.py +58 -0
- cortexops/cli.py +195 -0
- cortexops/client.py +84 -0
- cortexops/cortexops/__init__.py +58 -0
- cortexops/cortexops/cli.py +195 -0
- cortexops/cortexops/client.py +84 -0
- cortexops/cortexops/eval.py +216 -0
- cortexops/cortexops/judge.py +155 -0
- cortexops/cortexops/metrics.py +184 -0
- cortexops/cortexops/models.py +141 -0
- cortexops/cortexops/tracer.py +210 -0
- cortexops/eval.py +216 -0
- cortexops/judge.py +155 -0
- cortexops/metrics.py +184 -0
- cortexops/models.py +141 -0
- cortexops/pyproject.toml +87 -0
- cortexops/tests/__init__.py +0 -0
- cortexops/tests/test_cortexops.py +211 -0
- cortexops/tests/test_enhancements.py +222 -0
- cortexops/tracer.py +210 -0
- cortexops-0.1.0.dist-info/METADATA +169 -0
- cortexops-0.1.0.dist-info/RECORD +27 -0
- cortexops-0.1.0.dist-info/WHEEL +4 -0
- cortexops-0.1.0.dist-info/entry_points.txt +2 -0
- cortexops-0.1.0.dist-info/licenses/LICENSE +21 -0
cortexops/tracer.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
import uuid
|
|
5
|
+
from contextlib import contextmanager
|
|
6
|
+
from typing import Any, Callable
|
|
7
|
+
|
|
8
|
+
from .models import FailureKind, RunStatus, Trace, TraceNode, ToolCall, ToolCallStatus
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CortexTracer:
|
|
12
|
+
"""Instruments AI agents with zero-refactor tracing.
|
|
13
|
+
|
|
14
|
+
Supports LangGraph StateGraph and CrewAI Crew out of the box.
|
|
15
|
+
Falls back to a generic callable wrapper for any other agent type.
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
tracer = CortexTracer(project="payments-agent")
|
|
19
|
+
graph = tracer.wrap(your_langgraph_app)
|
|
20
|
+
result = graph.invoke({"messages": [...]})
|
|
21
|
+
trace = tracer.last_trace()
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
project: str,
|
|
27
|
+
api_key: str | None = None,
|
|
28
|
+
environment: str = "development",
|
|
29
|
+
sample_rate: float = 1.0,
|
|
30
|
+
local_store: bool = True,
|
|
31
|
+
) -> None:
|
|
32
|
+
self.project = project
|
|
33
|
+
self.api_key = api_key
|
|
34
|
+
self.environment = environment
|
|
35
|
+
self.sample_rate = sample_rate
|
|
36
|
+
self.local_store = local_store
|
|
37
|
+
self._traces: list[Trace] = []
|
|
38
|
+
self._current_trace: Trace | None = None
|
|
39
|
+
|
|
40
|
+
def wrap(self, agent: Any) -> Any:
|
|
41
|
+
"""Auto-detect agent type and return an instrumented wrapper."""
|
|
42
|
+
agent_type = type(agent).__name__
|
|
43
|
+
|
|
44
|
+
if agent_type == "CompiledStateGraph":
|
|
45
|
+
return self._wrap_langgraph(agent)
|
|
46
|
+
|
|
47
|
+
if agent_type == "Crew":
|
|
48
|
+
return self._wrap_crewai(agent)
|
|
49
|
+
|
|
50
|
+
if callable(agent) or hasattr(agent, "invoke"):
|
|
51
|
+
return self._wrap_callable(agent)
|
|
52
|
+
|
|
53
|
+
raise TypeError(
|
|
54
|
+
f"CortexTracer.wrap() does not support {agent_type}. "
|
|
55
|
+
"Pass a LangGraph CompiledStateGraph, CrewAI Crew, or any callable."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def _wrap_langgraph(self, graph: Any) -> Any:
|
|
59
|
+
tracer = self
|
|
60
|
+
|
|
61
|
+
class InstrumentedGraph:
|
|
62
|
+
def invoke(self_, input: dict, config: dict | None = None, **kwargs) -> dict:
|
|
63
|
+
return tracer._run_traced(
|
|
64
|
+
fn=lambda: graph.invoke(input, config, **kwargs),
|
|
65
|
+
input=input,
|
|
66
|
+
framework="langgraph",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
async def ainvoke(self_, input: dict, config: dict | None = None, **kwargs) -> dict:
|
|
70
|
+
import asyncio
|
|
71
|
+
return await asyncio.get_event_loop().run_in_executor(
|
|
72
|
+
None, lambda: tracer._run_traced(
|
|
73
|
+
fn=lambda: graph.invoke(input, config, **kwargs),
|
|
74
|
+
input=input,
|
|
75
|
+
framework="langgraph",
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def stream(self_, input: dict, config: dict | None = None, **kwargs):
|
|
80
|
+
return graph.stream(input, config, **kwargs)
|
|
81
|
+
|
|
82
|
+
def __getattr__(self_, name: str):
|
|
83
|
+
return getattr(graph, name)
|
|
84
|
+
|
|
85
|
+
return InstrumentedGraph()
|
|
86
|
+
|
|
87
|
+
def _wrap_crewai(self, crew: Any) -> Any:
|
|
88
|
+
tracer = self
|
|
89
|
+
|
|
90
|
+
class InstrumentedCrew:
|
|
91
|
+
def kickoff(self_, inputs: dict | None = None) -> Any:
|
|
92
|
+
return tracer._run_traced(
|
|
93
|
+
fn=lambda: crew.kickoff(inputs=inputs),
|
|
94
|
+
input=inputs or {},
|
|
95
|
+
framework="crewai",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def __getattr__(self_, name: str):
|
|
99
|
+
return getattr(crew, name)
|
|
100
|
+
|
|
101
|
+
return InstrumentedCrew()
|
|
102
|
+
|
|
103
|
+
def _wrap_callable(self, fn: Any) -> Any:
|
|
104
|
+
tracer = self
|
|
105
|
+
|
|
106
|
+
if hasattr(fn, "invoke"):
|
|
107
|
+
# Object with .invoke() — wrap that method
|
|
108
|
+
original_invoke = fn.invoke
|
|
109
|
+
|
|
110
|
+
class InvokeWrapper:
|
|
111
|
+
def invoke(self_, *args, **kwargs):
|
|
112
|
+
input_data = args[0] if args else kwargs
|
|
113
|
+
return tracer._run_traced(
|
|
114
|
+
fn=lambda: original_invoke(*args, **kwargs),
|
|
115
|
+
input=input_data if isinstance(input_data, dict) else {"input": input_data},
|
|
116
|
+
framework="generic",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def __getattr__(self_, name: str):
|
|
120
|
+
return getattr(fn, name)
|
|
121
|
+
|
|
122
|
+
return InvokeWrapper()
|
|
123
|
+
|
|
124
|
+
# Plain callable
|
|
125
|
+
def wrapper(*args, **kwargs):
|
|
126
|
+
input_data = {"args": list(args), "kwargs": kwargs}
|
|
127
|
+
return tracer._run_traced(fn=lambda: fn(*args, **kwargs), input=input_data, framework="generic")
|
|
128
|
+
|
|
129
|
+
return wrapper
|
|
130
|
+
|
|
131
|
+
def _run_traced(self, fn: Callable, input: dict, framework: str) -> Any:
|
|
132
|
+
trace = Trace(
|
|
133
|
+
project=self.project,
|
|
134
|
+
input=input,
|
|
135
|
+
)
|
|
136
|
+
self._current_trace = trace
|
|
137
|
+
t0 = time.perf_counter()
|
|
138
|
+
|
|
139
|
+
try:
|
|
140
|
+
result = fn()
|
|
141
|
+
trace.total_latency_ms = (time.perf_counter() - t0) * 1000
|
|
142
|
+
trace.status = RunStatus.COMPLETED
|
|
143
|
+
trace.output = result if isinstance(result, dict) else {"result": str(result)}
|
|
144
|
+
except Exception as exc:
|
|
145
|
+
trace.total_latency_ms = (time.perf_counter() - t0) * 1000
|
|
146
|
+
trace.status = RunStatus.FAILED
|
|
147
|
+
trace.failure_kind = FailureKind.UNKNOWN
|
|
148
|
+
trace.failure_detail = str(exc)
|
|
149
|
+
raise
|
|
150
|
+
finally:
|
|
151
|
+
self._traces.append(trace)
|
|
152
|
+
if self.api_key:
|
|
153
|
+
self._flush_trace(trace)
|
|
154
|
+
|
|
155
|
+
return result
|
|
156
|
+
|
|
157
|
+
@contextmanager
|
|
158
|
+
def trace_node(self, node_name: str):
|
|
159
|
+
"""Context manager to manually instrument a single node."""
|
|
160
|
+
node = TraceNode(node_id=str(uuid.uuid4()), node_name=node_name)
|
|
161
|
+
t0 = time.perf_counter()
|
|
162
|
+
try:
|
|
163
|
+
yield node
|
|
164
|
+
finally:
|
|
165
|
+
node.latency_ms = (time.perf_counter() - t0) * 1000
|
|
166
|
+
if self._current_trace:
|
|
167
|
+
self._current_trace.nodes.append(node)
|
|
168
|
+
|
|
169
|
+
def record_tool_call(
|
|
170
|
+
self,
|
|
171
|
+
name: str,
|
|
172
|
+
args: dict | None = None,
|
|
173
|
+
result: Any = None,
|
|
174
|
+
error: str | None = None,
|
|
175
|
+
latency_ms: float = 0.0,
|
|
176
|
+
) -> ToolCall:
|
|
177
|
+
"""Manually record a tool call onto the current active trace."""
|
|
178
|
+
tc = ToolCall(
|
|
179
|
+
name=name,
|
|
180
|
+
args=args or {},
|
|
181
|
+
result=result,
|
|
182
|
+
status=ToolCallStatus.ERROR if error else ToolCallStatus.SUCCESS,
|
|
183
|
+
latency_ms=latency_ms,
|
|
184
|
+
error=error,
|
|
185
|
+
)
|
|
186
|
+
if self._current_trace and self._current_trace.nodes:
|
|
187
|
+
self._current_trace.nodes[-1].tool_calls.append(tc)
|
|
188
|
+
return tc
|
|
189
|
+
|
|
190
|
+
def last_trace(self) -> Trace | None:
|
|
191
|
+
return self._traces[-1] if self._traces else None
|
|
192
|
+
|
|
193
|
+
def traces(self) -> list[Trace]:
|
|
194
|
+
return list(self._traces)
|
|
195
|
+
|
|
196
|
+
def clear(self) -> None:
|
|
197
|
+
self._traces.clear()
|
|
198
|
+
self._current_trace = None
|
|
199
|
+
|
|
200
|
+
def _flush_trace(self, trace: Trace) -> None:
|
|
201
|
+
try:
|
|
202
|
+
import httpx
|
|
203
|
+
httpx.post(
|
|
204
|
+
"https://api.cortexops.ai/v1/traces",
|
|
205
|
+
json=trace.model_dump(mode="json"),
|
|
206
|
+
headers={"Authorization": f"Bearer {self.api_key}"},
|
|
207
|
+
timeout=2.0,
|
|
208
|
+
)
|
|
209
|
+
except Exception:
|
|
210
|
+
pass # non-blocking — tracing never breaks the agent
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cortexops
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reliability infrastructure for AI agents — evaluation, observability, and regression testing
|
|
5
|
+
Project-URL: Homepage, https://cortexops.ai
|
|
6
|
+
Project-URL: Repository, https://github.com/ashishodu2023/cortexops
|
|
7
|
+
Project-URL: Documentation, https://docs.cortexops.ai
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/ashishodu2023/cortexops/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/ashishodu2023/cortexops/releases
|
|
10
|
+
Author-email: Ashish <ashishodu2023@gmail.com>
|
|
11
|
+
License: MIT License
|
|
12
|
+
|
|
13
|
+
Copyright (c) 2025 CortexOps Contributors
|
|
14
|
+
|
|
15
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
16
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
17
|
+
in the Software without restriction, including without limitation the rights
|
|
18
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
19
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
20
|
+
furnished to do so, subject to the following conditions:
|
|
21
|
+
|
|
22
|
+
The above copyright notice and this permission notice shall be included in all
|
|
23
|
+
copies or substantial portions of the Software.
|
|
24
|
+
|
|
25
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
26
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
27
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
28
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
29
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
31
|
+
SOFTWARE.
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Keywords: agents,ai,autogen,crewai,evaluation,langgraph,llm,observability,testing
|
|
34
|
+
Classifier: Development Status :: 3 - Alpha
|
|
35
|
+
Classifier: Intended Audience :: Developers
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Operating System :: OS Independent
|
|
38
|
+
Classifier: Programming Language :: Python :: 3
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
42
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
43
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
44
|
+
Classifier: Topic :: Software Development :: Testing
|
|
45
|
+
Classifier: Typing :: Typed
|
|
46
|
+
Requires-Python: >=3.10
|
|
47
|
+
Requires-Dist: pydantic>=2.0
|
|
48
|
+
Requires-Dist: pyyaml>=6.0
|
|
49
|
+
Requires-Dist: setuptools>=82.0.1
|
|
50
|
+
Provides-Extra: all
|
|
51
|
+
Requires-Dist: httpx>=0.27; extra == 'all'
|
|
52
|
+
Provides-Extra: dev
|
|
53
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
54
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
55
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
56
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
57
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
58
|
+
Provides-Extra: http
|
|
59
|
+
Requires-Dist: httpx>=0.27; extra == 'http'
|
|
60
|
+
Provides-Extra: llm
|
|
61
|
+
Requires-Dist: httpx>=0.27; extra == 'llm'
|
|
62
|
+
Description-Content-Type: text/markdown
|
|
63
|
+
|
|
64
|
+
# CortexOps
|
|
65
|
+
|
|
66
|
+
**Reliability infrastructure for AI agents.**
|
|
67
|
+
Evaluate · Observe · Operate — for LangGraph, CrewAI, and AutoGen.
|
|
68
|
+
|
|
69
|
+
[](https://pypi.org/project/cortexops/)
|
|
70
|
+
[](https://www.python.org/downloads/)
|
|
71
|
+
[](https://github.com/ashishodu2023/cortexops/actions/workflows/eval.yml)
|
|
72
|
+
[](https://github.com/ashishodu2023/cortexops/blob/main/LICENSE)
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## The problem
|
|
77
|
+
|
|
78
|
+
You deployed an agent. You have no idea if it regressed overnight.
|
|
79
|
+
|
|
80
|
+
No standard eval format. No failure traces. No CI gate before the next prompt change ships.
|
|
81
|
+
CortexOps fixes that.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Install
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pip install cortexops
|
|
89
|
+
|
|
90
|
+
# With HTTP client (for pushing traces to hosted API):
|
|
91
|
+
pip install cortexops[http]
|
|
92
|
+
|
|
93
|
+
# With LLM judge support:
|
|
94
|
+
pip install cortexops[llm]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Quickstart
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from cortexops import CortexTracer, EvalSuite
|
|
103
|
+
|
|
104
|
+
# Wrap your LangGraph app — zero refactor required
|
|
105
|
+
tracer = CortexTracer(project="payments-agent")
|
|
106
|
+
graph = tracer.wrap(your_langgraph_app)
|
|
107
|
+
|
|
108
|
+
# Run evaluations against a golden dataset
|
|
109
|
+
results = EvalSuite.run(
|
|
110
|
+
dataset="golden_v1.yaml",
|
|
111
|
+
agent=graph,
|
|
112
|
+
)
|
|
113
|
+
print(results.summary())
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Golden dataset (YAML)
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
version: 1
|
|
122
|
+
project: payments-agent
|
|
123
|
+
|
|
124
|
+
cases:
|
|
125
|
+
- id: refund_lookup_01
|
|
126
|
+
input: "What is the status of refund REF-8821?"
|
|
127
|
+
expected_tool_calls: [lookup_refund]
|
|
128
|
+
expected_output_contains: ["approved", "REF-8821"]
|
|
129
|
+
max_latency_ms: 3000
|
|
130
|
+
|
|
131
|
+
- id: open_ended_explanation_01
|
|
132
|
+
input: "Why was my refund rejected?"
|
|
133
|
+
judge: llm
|
|
134
|
+
judge_criteria: >
|
|
135
|
+
The response must explain the rejection reason clearly,
|
|
136
|
+
be empathetic, and offer a concrete next step. No jargon.
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## CI gate
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
cortexops eval run \
|
|
145
|
+
--dataset golden_v1.yaml \
|
|
146
|
+
--fail-on "task_completion < 0.90"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Exits non-zero if the threshold is not met — blocks the PR.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Built-in metrics
|
|
154
|
+
|
|
155
|
+
| Metric | What it checks |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `task_completion` | Non-empty, non-error output with expected content |
|
|
158
|
+
| `tool_accuracy` | Expected tool calls were actually made |
|
|
159
|
+
| `latency` | Response within `max_latency_ms` budget |
|
|
160
|
+
| `hallucination` | Fabrication signals in output |
|
|
161
|
+
| `llm_judge` | GPT-4o scores against natural-language criteria |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Links
|
|
166
|
+
|
|
167
|
+
- **Docs**: [docs.cortexops.ai](https://docs.cortexops.ai)
|
|
168
|
+
- **Repo**: [github.com/ashishodu2023/cortexops](https://github.com/ashishodu2023/cortexops)
|
|
169
|
+
- **Issues**: [GitHub Issues](https://github.com/ashishodu2023/cortexops/issues)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
cortexops/LICENSE,sha256=cXemgb-9EkUWB7EQ1riVRZn2gVQ1JcG7U34LpkUjaZA,1079
|
|
2
|
+
cortexops/README.md,sha256=ydnSnOOqE2BbXgpFiusbaNQORc69ANkhQoeoJZAmHhc,2720
|
|
3
|
+
cortexops/__init__.py,sha256=6IBgalIuUUlXz3TlliO3ERUpi-FJQwFE71CY1HY1C5s,1168
|
|
4
|
+
cortexops/cli.py,sha256=oZKt6Xow6srU3Xm5GJ0-5OsfQAyHphPxzFNJIqHLU0s,7281
|
|
5
|
+
cortexops/client.py,sha256=AE9hhcdlP2D-_QwQN0Qj4572WNqVykYTB0JOi7917R0,2754
|
|
6
|
+
cortexops/eval.py,sha256=BkVoYLDzx15ZhJ0V-whUROYqBvSUgM_3l10L7yQu5yA,7248
|
|
7
|
+
cortexops/judge.py,sha256=ILJNaTySkfqT-dAnafmr48bb9N9YiDXoNo_JmZLQUBM,5386
|
|
8
|
+
cortexops/metrics.py,sha256=BMK8I0ceabpo0yZvP5lVvL9lCPBCZ8yNptidYeXwIK8,6545
|
|
9
|
+
cortexops/models.py,sha256=9mx2ZUAJJyzSQXmTsVqJfLRLorRyuQ_MIhmnHrXYABE,4441
|
|
10
|
+
cortexops/pyproject.toml,sha256=rDbZVYog_dZ7gg7mR_RVNgCMNulvcn70nK7AIIArbl8,2164
|
|
11
|
+
cortexops/tracer.py,sha256=ySoDkUZwkSzWUnk9cs9puf_Z3Wz4HdvGhxbOSbjIlcw,7108
|
|
12
|
+
cortexops/cortexops/__init__.py,sha256=6IBgalIuUUlXz3TlliO3ERUpi-FJQwFE71CY1HY1C5s,1168
|
|
13
|
+
cortexops/cortexops/cli.py,sha256=oZKt6Xow6srU3Xm5GJ0-5OsfQAyHphPxzFNJIqHLU0s,7281
|
|
14
|
+
cortexops/cortexops/client.py,sha256=AE9hhcdlP2D-_QwQN0Qj4572WNqVykYTB0JOi7917R0,2754
|
|
15
|
+
cortexops/cortexops/eval.py,sha256=BkVoYLDzx15ZhJ0V-whUROYqBvSUgM_3l10L7yQu5yA,7248
|
|
16
|
+
cortexops/cortexops/judge.py,sha256=ILJNaTySkfqT-dAnafmr48bb9N9YiDXoNo_JmZLQUBM,5386
|
|
17
|
+
cortexops/cortexops/metrics.py,sha256=BMK8I0ceabpo0yZvP5lVvL9lCPBCZ8yNptidYeXwIK8,6545
|
|
18
|
+
cortexops/cortexops/models.py,sha256=9mx2ZUAJJyzSQXmTsVqJfLRLorRyuQ_MIhmnHrXYABE,4441
|
|
19
|
+
cortexops/cortexops/tracer.py,sha256=ySoDkUZwkSzWUnk9cs9puf_Z3Wz4HdvGhxbOSbjIlcw,7108
|
|
20
|
+
cortexops/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
cortexops/tests/test_cortexops.py,sha256=SVoeR3ZfCca4J0Kg4-HM4_MPmoTGSElVcPC-OQqdYRM,7967
|
|
22
|
+
cortexops/tests/test_enhancements.py,sha256=gqxrwuF9EuDxunLlqNFmwSCoq9gm0BrYN81ZgM3WBLc,8238
|
|
23
|
+
cortexops-0.1.0.dist-info/METADATA,sha256=UCATLqZfsI3Y9RMj99ZjMmS2MvJqUNVswfO0byXpgAo,5783
|
|
24
|
+
cortexops-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
25
|
+
cortexops-0.1.0.dist-info/entry_points.txt,sha256=wBc4X1RuV2sUDyF5TLPmsRrJQ3GrbewjPeC0K0C6r0k,49
|
|
26
|
+
cortexops-0.1.0.dist-info/licenses/LICENSE,sha256=cXemgb-9EkUWB7EQ1riVRZn2gVQ1JcG7U34LpkUjaZA,1079
|
|
27
|
+
cortexops-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 CortexOps Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|