avenix 0.1.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.
- avenix-0.1.0/LICENSE +21 -0
- avenix-0.1.0/PKG-INFO +247 -0
- avenix-0.1.0/README.md +217 -0
- avenix-0.1.0/avenix/__init__.py +11 -0
- avenix-0.1.0/avenix/decorator.py +62 -0
- avenix-0.1.0/avenix/extractors.py +81 -0
- avenix-0.1.0/avenix/formatter.py +71 -0
- avenix-0.1.0/avenix/logger.py +29 -0
- avenix-0.1.0/avenix/models.py +97 -0
- avenix-0.1.0/avenix/tracer.py +215 -0
- avenix-0.1.0/avenix.egg-info/PKG-INFO +247 -0
- avenix-0.1.0/avenix.egg-info/SOURCES.txt +23 -0
- avenix-0.1.0/avenix.egg-info/dependency_links.txt +1 -0
- avenix-0.1.0/avenix.egg-info/requires.txt +8 -0
- avenix-0.1.0/avenix.egg-info/top_level.txt +1 -0
- avenix-0.1.0/pyproject.toml +75 -0
- avenix-0.1.0/setup.cfg +4 -0
- avenix-0.1.0/tests/test_decorator.py +306 -0
- avenix-0.1.0/tests/test_extractors.py +266 -0
- avenix-0.1.0/tests/test_formatter.py +226 -0
- avenix-0.1.0/tests/test_imports.py +38 -0
- avenix-0.1.0/tests/test_integration.py +230 -0
- avenix-0.1.0/tests/test_logger.py +141 -0
- avenix-0.1.0/tests/test_models.py +290 -0
- avenix-0.1.0/tests/test_tracer.py +734 -0
avenix-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 AgentForge 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.
|
avenix-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: avenix
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Avenix is a focused Python tracing library for AI requests with beautiful terminal output
|
|
5
|
+
Author: Avenix Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/avenix/avenix
|
|
8
|
+
Project-URL: Repository, https://github.com/avenix/avenix
|
|
9
|
+
Project-URL: Documentation, https://github.com/avenix/avenix#readme
|
|
10
|
+
Keywords: ai,tracing,llm,monitoring,openai,anthropic
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Topic :: System :: Monitoring
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: pydantic<3.0,>=2.0
|
|
23
|
+
Requires-Dist: rich<14.0,>=13.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest<9.0,>=8.0; extra == "dev"
|
|
26
|
+
Requires-Dist: hypothesis<7.0,>=6.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-cov<5.0,>=4.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-mock<4.0,>=3.0; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# Avenix v0.1
|
|
32
|
+
|
|
33
|
+
A Python tracing library for AI/LLM requests with beautiful terminal output.
|
|
34
|
+
|
|
35
|
+
Avenix provides a decorator-based API for tracing AI model requests, automatically capturing execution metrics like timing, token usage, and costs, then displaying them in richly formatted terminal output.
|
|
36
|
+
|
|
37
|
+
## Overview
|
|
38
|
+
|
|
39
|
+
Avenix simplifies monitoring AI/LLM requests by:
|
|
40
|
+
- **Automatic Capture**: Uses a simple `@trace` decorator to automatically capture request metrics
|
|
41
|
+
- **Beautiful Display**: Renders trace information in a formatted terminal panel with colors and separators
|
|
42
|
+
- **Multi-Provider Support**: Works with OpenAI and Anthropic model responses out of the box
|
|
43
|
+
- **Cost Tracking**: Automatically calculates request costs based on model pricing
|
|
44
|
+
- **Extensible**: Easy to add custom extractors for new AI providers
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install avenix
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Requirements
|
|
53
|
+
|
|
54
|
+
- Python 3.11+
|
|
55
|
+
- pydantic ^2.0
|
|
56
|
+
- rich ^13.0
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
### Using the @trace Decorator
|
|
61
|
+
|
|
62
|
+
The simplest way to use Avenix is with the `@trace` decorator:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from avenix import trace
|
|
66
|
+
from openai import OpenAI
|
|
67
|
+
|
|
68
|
+
client = OpenAI()
|
|
69
|
+
|
|
70
|
+
@trace
|
|
71
|
+
def get_gpt_response(prompt: str):
|
|
72
|
+
"""Call GPT-4 with the given prompt."""
|
|
73
|
+
response = client.chat.completions.create(
|
|
74
|
+
model="gpt-4",
|
|
75
|
+
messages=[{"role": "user", "content": prompt}]
|
|
76
|
+
)
|
|
77
|
+
return response
|
|
78
|
+
|
|
79
|
+
# When you call the function, Avenix will automatically:
|
|
80
|
+
# 1. Measure execution time
|
|
81
|
+
# 2. Extract model, tokens, and response from the result
|
|
82
|
+
# 3. Calculate cost based on token usage
|
|
83
|
+
# 4. Display formatted trace output to terminal
|
|
84
|
+
result = get_gpt_response("What is machine learning?")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Manual Trace Creation
|
|
88
|
+
|
|
89
|
+
For more control, you can manually create traces using the `Tracer` API:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from avenix import Tracer
|
|
93
|
+
|
|
94
|
+
tracer = Tracer()
|
|
95
|
+
|
|
96
|
+
# Later, manually create a trace with explicit values
|
|
97
|
+
tracer.create_trace(
|
|
98
|
+
model="gpt-4",
|
|
99
|
+
latency=2.5,
|
|
100
|
+
input_tokens=150,
|
|
101
|
+
output_tokens=300,
|
|
102
|
+
cost=0.045,
|
|
103
|
+
prompt="What is AI?",
|
|
104
|
+
response="AI is artificial intelligence..."
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Supported Models
|
|
109
|
+
|
|
110
|
+
Avenix includes built-in support for:
|
|
111
|
+
|
|
112
|
+
### OpenAI
|
|
113
|
+
- gpt-4
|
|
114
|
+
- gpt-4-turbo
|
|
115
|
+
- gpt-3.5-turbo
|
|
116
|
+
|
|
117
|
+
### Anthropic
|
|
118
|
+
- claude-3-opus
|
|
119
|
+
- claude-3-sonnet
|
|
120
|
+
- claude-3-haiku
|
|
121
|
+
|
|
122
|
+
## Features in v0.1
|
|
123
|
+
|
|
124
|
+
✅ Decorator-based tracing API
|
|
125
|
+
✅ Automatic timing measurement with perf_counter
|
|
126
|
+
✅ OpenAI and Anthropic response extraction
|
|
127
|
+
✅ Model pricing table and cost calculation
|
|
128
|
+
✅ Beautiful terminal output with rich formatting
|
|
129
|
+
✅ Error handling and graceful fallbacks
|
|
130
|
+
✅ Property-based test suite for correctness verification
|
|
131
|
+
|
|
132
|
+
## Out of Scope for v0.1
|
|
133
|
+
|
|
134
|
+
The following features are planned for future releases:
|
|
135
|
+
|
|
136
|
+
- Custom formatter plugins
|
|
137
|
+
- Database/file persistence of traces
|
|
138
|
+
- Trace filtering and search
|
|
139
|
+
- Performance statistics aggregation
|
|
140
|
+
- Integration with external logging services
|
|
141
|
+
- Support for additional AI providers
|
|
142
|
+
- Rate limiting and quota management
|
|
143
|
+
- Async/await support
|
|
144
|
+
|
|
145
|
+
## Documentation
|
|
146
|
+
|
|
147
|
+
### API Reference
|
|
148
|
+
|
|
149
|
+
#### @trace Decorator
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
@trace
|
|
153
|
+
def your_function():
|
|
154
|
+
# ... your code that calls an AI model
|
|
155
|
+
return response
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The `@trace` decorator:
|
|
159
|
+
- Measures execution time with `time.perf_counter()`
|
|
160
|
+
- Captures the function result
|
|
161
|
+
- Calls the global `Tracer` instance to extract data and display trace
|
|
162
|
+
- Propagates exceptions without suppression
|
|
163
|
+
- Preserves the original function's return value and metadata
|
|
164
|
+
|
|
165
|
+
#### Tracer Class
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
from avenix import Tracer
|
|
169
|
+
|
|
170
|
+
tracer = Tracer(logger=None, formatter=None)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Methods:
|
|
174
|
+
- `capture_trace(result, latency, func_name=None)`: Capture and display a trace from function execution
|
|
175
|
+
- `create_trace(model, latency, input_tokens, output_tokens, cost, prompt, response)`: Manually create and display a trace
|
|
176
|
+
|
|
177
|
+
### TraceModel
|
|
178
|
+
|
|
179
|
+
The `TraceModel` class represents a single trace with validation:
|
|
180
|
+
|
|
181
|
+
Fields:
|
|
182
|
+
- `model` (str): Name of the AI model
|
|
183
|
+
- `latency` (float): Execution time in seconds (rounded to 2 decimals)
|
|
184
|
+
- `input_tokens` (int): Number of input tokens (must be non-negative)
|
|
185
|
+
- `output_tokens` (int): Number of output tokens (must be non-negative)
|
|
186
|
+
- `cost` (float): Request cost in dollars (rounded to 4 decimals, must be non-negative)
|
|
187
|
+
- `prompt` (str): Input prompt text (defaults to empty string)
|
|
188
|
+
- `response` (str): Model response text (defaults to empty string)
|
|
189
|
+
|
|
190
|
+
## Examples
|
|
191
|
+
|
|
192
|
+
See the `examples/` directory for complete working examples:
|
|
193
|
+
|
|
194
|
+
- `openai_example.py`: Using @trace with OpenAI API
|
|
195
|
+
- `anthropic_example.py`: Using @trace with Anthropic API
|
|
196
|
+
- `manual_trace.py`: Creating traces manually with Tracer API
|
|
197
|
+
|
|
198
|
+
## Testing
|
|
199
|
+
|
|
200
|
+
Avenix includes a comprehensive test suite with property-based tests:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
pytest tests/ -v # Run all tests
|
|
204
|
+
pytest tests/ --cov # Run with coverage report
|
|
205
|
+
pytest tests/test_models.py -v # Run specific test file
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Test coverage targets:
|
|
209
|
+
- Core modules (decorator, tracer, models, formatter, extractors): >90%
|
|
210
|
+
- Property-based tests for 16 correctness properties
|
|
211
|
+
- Unit tests for all feature components
|
|
212
|
+
|
|
213
|
+
## Architecture
|
|
214
|
+
|
|
215
|
+
Avenix follows a layered architecture:
|
|
216
|
+
|
|
217
|
+
1. **Models Layer** (`models.py`): Defines `TraceModel` with Pydantic validation
|
|
218
|
+
2. **Decorator Layer** (`decorator.py`): Provides `@trace` decorator for wrapping functions
|
|
219
|
+
3. **Tracer Layer** (`tracer.py`): Core orchestration with optional custom logger/formatter
|
|
220
|
+
4. **Extraction Layer** (`extractors.py`): Provider-specific response extractors
|
|
221
|
+
5. **Formatting Layer** (`formatter.py`): Beautiful terminal output with rich library
|
|
222
|
+
6. **Logging Layer** (`logger.py`): Terminal display with fallback handling
|
|
223
|
+
|
|
224
|
+
## Error Handling
|
|
225
|
+
|
|
226
|
+
Avenix is designed to be resilient to errors:
|
|
227
|
+
|
|
228
|
+
- **Extraction Failures**: If response format doesn't match known providers, uses sensible defaults
|
|
229
|
+
- **Validation Failures**: If TraceModel validation fails, falls back to defaults
|
|
230
|
+
- **Rendering Failures**: If rich formatting fails, falls back to basic print output
|
|
231
|
+
- **Exception Propagation**: Errors in traced functions propagate normally without suppression
|
|
232
|
+
|
|
233
|
+
## Contributing
|
|
234
|
+
|
|
235
|
+
This is a v0.1 release. Feedback and contributions are welcome!
|
|
236
|
+
|
|
237
|
+
## API Reference
|
|
238
|
+
|
|
239
|
+
For detailed API documentation, see [API.md](API.md).
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
MIT License - See LICENSE file for details
|
|
244
|
+
|
|
245
|
+
## Changelog
|
|
246
|
+
|
|
247
|
+
See CHANGELOG.md for version history and release notes
|
avenix-0.1.0/README.md
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Avenix v0.1
|
|
2
|
+
|
|
3
|
+
A Python tracing library for AI/LLM requests with beautiful terminal output.
|
|
4
|
+
|
|
5
|
+
Avenix provides a decorator-based API for tracing AI model requests, automatically capturing execution metrics like timing, token usage, and costs, then displaying them in richly formatted terminal output.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
Avenix simplifies monitoring AI/LLM requests by:
|
|
10
|
+
- **Automatic Capture**: Uses a simple `@trace` decorator to automatically capture request metrics
|
|
11
|
+
- **Beautiful Display**: Renders trace information in a formatted terminal panel with colors and separators
|
|
12
|
+
- **Multi-Provider Support**: Works with OpenAI and Anthropic model responses out of the box
|
|
13
|
+
- **Cost Tracking**: Automatically calculates request costs based on model pricing
|
|
14
|
+
- **Extensible**: Easy to add custom extractors for new AI providers
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install avenix
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- Python 3.11+
|
|
25
|
+
- pydantic ^2.0
|
|
26
|
+
- rich ^13.0
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### Using the @trace Decorator
|
|
31
|
+
|
|
32
|
+
The simplest way to use Avenix is with the `@trace` decorator:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from avenix import trace
|
|
36
|
+
from openai import OpenAI
|
|
37
|
+
|
|
38
|
+
client = OpenAI()
|
|
39
|
+
|
|
40
|
+
@trace
|
|
41
|
+
def get_gpt_response(prompt: str):
|
|
42
|
+
"""Call GPT-4 with the given prompt."""
|
|
43
|
+
response = client.chat.completions.create(
|
|
44
|
+
model="gpt-4",
|
|
45
|
+
messages=[{"role": "user", "content": prompt}]
|
|
46
|
+
)
|
|
47
|
+
return response
|
|
48
|
+
|
|
49
|
+
# When you call the function, Avenix will automatically:
|
|
50
|
+
# 1. Measure execution time
|
|
51
|
+
# 2. Extract model, tokens, and response from the result
|
|
52
|
+
# 3. Calculate cost based on token usage
|
|
53
|
+
# 4. Display formatted trace output to terminal
|
|
54
|
+
result = get_gpt_response("What is machine learning?")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Manual Trace Creation
|
|
58
|
+
|
|
59
|
+
For more control, you can manually create traces using the `Tracer` API:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from avenix import Tracer
|
|
63
|
+
|
|
64
|
+
tracer = Tracer()
|
|
65
|
+
|
|
66
|
+
# Later, manually create a trace with explicit values
|
|
67
|
+
tracer.create_trace(
|
|
68
|
+
model="gpt-4",
|
|
69
|
+
latency=2.5,
|
|
70
|
+
input_tokens=150,
|
|
71
|
+
output_tokens=300,
|
|
72
|
+
cost=0.045,
|
|
73
|
+
prompt="What is AI?",
|
|
74
|
+
response="AI is artificial intelligence..."
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Supported Models
|
|
79
|
+
|
|
80
|
+
Avenix includes built-in support for:
|
|
81
|
+
|
|
82
|
+
### OpenAI
|
|
83
|
+
- gpt-4
|
|
84
|
+
- gpt-4-turbo
|
|
85
|
+
- gpt-3.5-turbo
|
|
86
|
+
|
|
87
|
+
### Anthropic
|
|
88
|
+
- claude-3-opus
|
|
89
|
+
- claude-3-sonnet
|
|
90
|
+
- claude-3-haiku
|
|
91
|
+
|
|
92
|
+
## Features in v0.1
|
|
93
|
+
|
|
94
|
+
✅ Decorator-based tracing API
|
|
95
|
+
✅ Automatic timing measurement with perf_counter
|
|
96
|
+
✅ OpenAI and Anthropic response extraction
|
|
97
|
+
✅ Model pricing table and cost calculation
|
|
98
|
+
✅ Beautiful terminal output with rich formatting
|
|
99
|
+
✅ Error handling and graceful fallbacks
|
|
100
|
+
✅ Property-based test suite for correctness verification
|
|
101
|
+
|
|
102
|
+
## Out of Scope for v0.1
|
|
103
|
+
|
|
104
|
+
The following features are planned for future releases:
|
|
105
|
+
|
|
106
|
+
- Custom formatter plugins
|
|
107
|
+
- Database/file persistence of traces
|
|
108
|
+
- Trace filtering and search
|
|
109
|
+
- Performance statistics aggregation
|
|
110
|
+
- Integration with external logging services
|
|
111
|
+
- Support for additional AI providers
|
|
112
|
+
- Rate limiting and quota management
|
|
113
|
+
- Async/await support
|
|
114
|
+
|
|
115
|
+
## Documentation
|
|
116
|
+
|
|
117
|
+
### API Reference
|
|
118
|
+
|
|
119
|
+
#### @trace Decorator
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
@trace
|
|
123
|
+
def your_function():
|
|
124
|
+
# ... your code that calls an AI model
|
|
125
|
+
return response
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The `@trace` decorator:
|
|
129
|
+
- Measures execution time with `time.perf_counter()`
|
|
130
|
+
- Captures the function result
|
|
131
|
+
- Calls the global `Tracer` instance to extract data and display trace
|
|
132
|
+
- Propagates exceptions without suppression
|
|
133
|
+
- Preserves the original function's return value and metadata
|
|
134
|
+
|
|
135
|
+
#### Tracer Class
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from avenix import Tracer
|
|
139
|
+
|
|
140
|
+
tracer = Tracer(logger=None, formatter=None)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Methods:
|
|
144
|
+
- `capture_trace(result, latency, func_name=None)`: Capture and display a trace from function execution
|
|
145
|
+
- `create_trace(model, latency, input_tokens, output_tokens, cost, prompt, response)`: Manually create and display a trace
|
|
146
|
+
|
|
147
|
+
### TraceModel
|
|
148
|
+
|
|
149
|
+
The `TraceModel` class represents a single trace with validation:
|
|
150
|
+
|
|
151
|
+
Fields:
|
|
152
|
+
- `model` (str): Name of the AI model
|
|
153
|
+
- `latency` (float): Execution time in seconds (rounded to 2 decimals)
|
|
154
|
+
- `input_tokens` (int): Number of input tokens (must be non-negative)
|
|
155
|
+
- `output_tokens` (int): Number of output tokens (must be non-negative)
|
|
156
|
+
- `cost` (float): Request cost in dollars (rounded to 4 decimals, must be non-negative)
|
|
157
|
+
- `prompt` (str): Input prompt text (defaults to empty string)
|
|
158
|
+
- `response` (str): Model response text (defaults to empty string)
|
|
159
|
+
|
|
160
|
+
## Examples
|
|
161
|
+
|
|
162
|
+
See the `examples/` directory for complete working examples:
|
|
163
|
+
|
|
164
|
+
- `openai_example.py`: Using @trace with OpenAI API
|
|
165
|
+
- `anthropic_example.py`: Using @trace with Anthropic API
|
|
166
|
+
- `manual_trace.py`: Creating traces manually with Tracer API
|
|
167
|
+
|
|
168
|
+
## Testing
|
|
169
|
+
|
|
170
|
+
Avenix includes a comprehensive test suite with property-based tests:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
pytest tests/ -v # Run all tests
|
|
174
|
+
pytest tests/ --cov # Run with coverage report
|
|
175
|
+
pytest tests/test_models.py -v # Run specific test file
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Test coverage targets:
|
|
179
|
+
- Core modules (decorator, tracer, models, formatter, extractors): >90%
|
|
180
|
+
- Property-based tests for 16 correctness properties
|
|
181
|
+
- Unit tests for all feature components
|
|
182
|
+
|
|
183
|
+
## Architecture
|
|
184
|
+
|
|
185
|
+
Avenix follows a layered architecture:
|
|
186
|
+
|
|
187
|
+
1. **Models Layer** (`models.py`): Defines `TraceModel` with Pydantic validation
|
|
188
|
+
2. **Decorator Layer** (`decorator.py`): Provides `@trace` decorator for wrapping functions
|
|
189
|
+
3. **Tracer Layer** (`tracer.py`): Core orchestration with optional custom logger/formatter
|
|
190
|
+
4. **Extraction Layer** (`extractors.py`): Provider-specific response extractors
|
|
191
|
+
5. **Formatting Layer** (`formatter.py`): Beautiful terminal output with rich library
|
|
192
|
+
6. **Logging Layer** (`logger.py`): Terminal display with fallback handling
|
|
193
|
+
|
|
194
|
+
## Error Handling
|
|
195
|
+
|
|
196
|
+
Avenix is designed to be resilient to errors:
|
|
197
|
+
|
|
198
|
+
- **Extraction Failures**: If response format doesn't match known providers, uses sensible defaults
|
|
199
|
+
- **Validation Failures**: If TraceModel validation fails, falls back to defaults
|
|
200
|
+
- **Rendering Failures**: If rich formatting fails, falls back to basic print output
|
|
201
|
+
- **Exception Propagation**: Errors in traced functions propagate normally without suppression
|
|
202
|
+
|
|
203
|
+
## Contributing
|
|
204
|
+
|
|
205
|
+
This is a v0.1 release. Feedback and contributions are welcome!
|
|
206
|
+
|
|
207
|
+
## API Reference
|
|
208
|
+
|
|
209
|
+
For detailed API documentation, see [API.md](API.md).
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
MIT License - See LICENSE file for details
|
|
214
|
+
|
|
215
|
+
## Changelog
|
|
216
|
+
|
|
217
|
+
See CHANGELOG.md for version history and release notes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Avenix - Python tracing library for AI requests.
|
|
3
|
+
|
|
4
|
+
Provides decorator-based tracing for AI/LLM requests with beautiful terminal output.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .decorator import trace
|
|
8
|
+
from .tracer import Tracer
|
|
9
|
+
|
|
10
|
+
__version__ = "0.1.0"
|
|
11
|
+
__all__ = ["trace", "Tracer"]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Trace decorator implementation
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import time
|
|
5
|
+
from typing import Callable, Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def trace(func: Callable) -> Callable:
|
|
9
|
+
"""
|
|
10
|
+
Decorator that traces AI request function execution.
|
|
11
|
+
|
|
12
|
+
Captures timing, model info, tokens, cost, prompt, and response.
|
|
13
|
+
Displays formatted trace to terminal after execution.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
func: The function to trace (should return AI response object)
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
Wrapped function with identical signature and return type
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
@trace
|
|
23
|
+
def call_openai(prompt: str):
|
|
24
|
+
return client.chat.completions.create(
|
|
25
|
+
model="gpt-4",
|
|
26
|
+
messages=[{"role": "user", "content": prompt}]
|
|
27
|
+
)
|
|
28
|
+
"""
|
|
29
|
+
from .tracer import Tracer
|
|
30
|
+
|
|
31
|
+
# Create tracer instance once at decoration time
|
|
32
|
+
tracer = Tracer()
|
|
33
|
+
|
|
34
|
+
@functools.wraps(func)
|
|
35
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
36
|
+
# Record start time with high precision
|
|
37
|
+
start_time = time.perf_counter()
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
# Execute the wrapped function
|
|
41
|
+
result = func(*args, **kwargs)
|
|
42
|
+
|
|
43
|
+
# Calculate latency
|
|
44
|
+
end_time = time.perf_counter()
|
|
45
|
+
latency = end_time - start_time
|
|
46
|
+
|
|
47
|
+
# Capture and display trace
|
|
48
|
+
tracer.capture_trace(
|
|
49
|
+
result=result,
|
|
50
|
+
latency=latency,
|
|
51
|
+
func_name=func.__name__
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Return original result unchanged
|
|
55
|
+
return result
|
|
56
|
+
|
|
57
|
+
except Exception:
|
|
58
|
+
# Propagate exceptions without suppression
|
|
59
|
+
# No trace is captured on failure
|
|
60
|
+
raise
|
|
61
|
+
|
|
62
|
+
return wrapper
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Provider-specific extraction logic for AI responses
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ResponseExtractor(ABC):
|
|
11
|
+
"""Abstract base class for AI response extractors."""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def can_extract(self, result: Any) -> bool:
|
|
15
|
+
"""Check if this extractor can handle the given result."""
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def extract(self, result: Any) -> Dict[str, Any]:
|
|
20
|
+
"""Extract trace data from result."""
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class OpenAIExtractor(ResponseExtractor):
|
|
25
|
+
"""Extractor for OpenAI response format."""
|
|
26
|
+
|
|
27
|
+
def can_extract(self, result: Any) -> bool:
|
|
28
|
+
"""Check if result matches OpenAI format."""
|
|
29
|
+
usage = getattr(result, 'usage', None)
|
|
30
|
+
choices = getattr(result, 'choices', None)
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
hasattr(result, 'model') and
|
|
34
|
+
hasattr(usage, 'prompt_tokens') and
|
|
35
|
+
hasattr(usage, 'completion_tokens') and
|
|
36
|
+
isinstance(choices, (list, tuple))
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def extract(self, result: Any) -> Dict[str, Any]:
|
|
40
|
+
"""Extract trace data from OpenAI response."""
|
|
41
|
+
try:
|
|
42
|
+
return {
|
|
43
|
+
'model': result.model,
|
|
44
|
+
'input_tokens': result.usage.prompt_tokens,
|
|
45
|
+
'output_tokens': result.usage.completion_tokens,
|
|
46
|
+
'prompt': '', # Not available in response
|
|
47
|
+
'response': result.choices[0].message.content
|
|
48
|
+
}
|
|
49
|
+
except (AttributeError, IndexError) as e:
|
|
50
|
+
logger.warning(f"Failed to extract OpenAI trace data: {e}")
|
|
51
|
+
return {}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class AnthropicExtractor(ResponseExtractor):
|
|
55
|
+
"""Extractor for Anthropic response format."""
|
|
56
|
+
|
|
57
|
+
def can_extract(self, result: Any) -> bool:
|
|
58
|
+
"""Check if result matches Anthropic format."""
|
|
59
|
+
usage = getattr(result, 'usage', None)
|
|
60
|
+
content = getattr(result, 'content', None)
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
hasattr(result, 'model') and
|
|
64
|
+
hasattr(usage, 'input_tokens') and
|
|
65
|
+
hasattr(usage, 'output_tokens') and
|
|
66
|
+
isinstance(content, (list, tuple))
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def extract(self, result: Any) -> Dict[str, Any]:
|
|
70
|
+
"""Extract trace data from Anthropic response."""
|
|
71
|
+
try:
|
|
72
|
+
return {
|
|
73
|
+
'model': result.model,
|
|
74
|
+
'input_tokens': result.usage.input_tokens,
|
|
75
|
+
'output_tokens': result.usage.output_tokens,
|
|
76
|
+
'prompt': '', # Not available in response
|
|
77
|
+
'response': result.content[0].text if result.content else ''
|
|
78
|
+
}
|
|
79
|
+
except (AttributeError, IndexError) as e:
|
|
80
|
+
logger.warning(f"Failed to extract Anthropic trace data: {e}")
|
|
81
|
+
return {}
|