fluxloop 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.
Potentially problematic release.
This version of fluxloop might be problematic. Click here for more details.
- fluxloop/__init__.py +58 -0
- fluxloop/buffer.py +186 -0
- fluxloop/client.py +175 -0
- fluxloop/config.py +191 -0
- fluxloop/context.py +219 -0
- fluxloop/decorators.py +465 -0
- fluxloop/models.py +92 -0
- fluxloop/recording.py +205 -0
- fluxloop/schemas/__init__.py +48 -0
- fluxloop/schemas/config.py +312 -0
- fluxloop/schemas/trace.py +197 -0
- fluxloop/serialization.py +116 -0
- fluxloop/storage.py +53 -0
- fluxloop-0.1.0.dist-info/METADATA +76 -0
- fluxloop-0.1.0.dist-info/RECORD +17 -0
- fluxloop-0.1.0.dist-info/WHEEL +5 -0
- fluxloop-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Trace and Observation models based on Langfuse data model.
|
|
3
|
+
|
|
4
|
+
References:
|
|
5
|
+
- https://langfuse.com/docs/observability/data-model
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from typing import Any, Dict, List, Optional, Union
|
|
11
|
+
from uuid import UUID, uuid4
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field, field_validator
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TraceStatus(str, Enum):
|
|
17
|
+
"""Status of a trace execution."""
|
|
18
|
+
|
|
19
|
+
PENDING = "pending"
|
|
20
|
+
RUNNING = "running"
|
|
21
|
+
SUCCESS = "success"
|
|
22
|
+
FAILED = "failed"
|
|
23
|
+
CANCELLED = "cancelled"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ObservationType(str, Enum):
|
|
27
|
+
"""Type of observation in the trace hierarchy."""
|
|
28
|
+
|
|
29
|
+
SPAN = "span" # Generic span
|
|
30
|
+
EVENT = "event" # Single event
|
|
31
|
+
GENERATION = "generation" # LLM generation
|
|
32
|
+
TOOL = "tool" # Tool/function call
|
|
33
|
+
AGENT = "agent" # Agent execution
|
|
34
|
+
CHAIN = "chain" # Chain of operations
|
|
35
|
+
EVALUATION = "evaluation" # Evaluation result
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ObservationLevel(str, Enum):
|
|
39
|
+
"""Log level for observations."""
|
|
40
|
+
|
|
41
|
+
DEBUG = "debug"
|
|
42
|
+
INFO = "info"
|
|
43
|
+
WARNING = "warning"
|
|
44
|
+
ERROR = "error"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ScoreDataType(str, Enum):
|
|
48
|
+
"""Data type of score values."""
|
|
49
|
+
|
|
50
|
+
NUMERIC = "numeric"
|
|
51
|
+
BOOLEAN = "boolean"
|
|
52
|
+
CATEGORICAL = "categorical"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class Score(BaseModel):
|
|
56
|
+
"""Score/evaluation result for a trace or observation."""
|
|
57
|
+
|
|
58
|
+
id: UUID = Field(default_factory=uuid4)
|
|
59
|
+
trace_id: UUID
|
|
60
|
+
observation_id: Optional[UUID] = None
|
|
61
|
+
name: str # e.g., "accuracy", "relevance", "latency"
|
|
62
|
+
value: Union[float, bool, str]
|
|
63
|
+
data_type: ScoreDataType
|
|
64
|
+
comment: Optional[str] = None
|
|
65
|
+
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
66
|
+
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
67
|
+
|
|
68
|
+
@field_validator("value")
|
|
69
|
+
def validate_value_type(cls, v, info):
|
|
70
|
+
"""Ensure value matches the declared data type."""
|
|
71
|
+
data_type = info.data.get("data_type")
|
|
72
|
+
if data_type == ScoreDataType.NUMERIC and not isinstance(v, (int, float)):
|
|
73
|
+
raise ValueError("Numeric score must be int or float")
|
|
74
|
+
elif data_type == ScoreDataType.BOOLEAN and not isinstance(v, bool):
|
|
75
|
+
raise ValueError("Boolean score must be bool")
|
|
76
|
+
elif data_type == ScoreDataType.CATEGORICAL and not isinstance(v, str):
|
|
77
|
+
raise ValueError("Categorical score must be str")
|
|
78
|
+
return v
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Observation(BaseModel):
|
|
82
|
+
"""Single observation within a trace."""
|
|
83
|
+
|
|
84
|
+
id: UUID = Field(default_factory=uuid4)
|
|
85
|
+
trace_id: UUID
|
|
86
|
+
parent_observation_id: Optional[UUID] = None
|
|
87
|
+
type: ObservationType
|
|
88
|
+
name: str
|
|
89
|
+
|
|
90
|
+
# Timing
|
|
91
|
+
start_time: datetime = Field(default_factory=datetime.utcnow)
|
|
92
|
+
end_time: Optional[datetime] = None
|
|
93
|
+
|
|
94
|
+
# Content
|
|
95
|
+
input: Optional[Any] = None
|
|
96
|
+
output: Optional[Any] = None
|
|
97
|
+
error: Optional[str] = None
|
|
98
|
+
|
|
99
|
+
# Metadata
|
|
100
|
+
level: ObservationLevel = ObservationLevel.INFO
|
|
101
|
+
status_message: Optional[str] = None
|
|
102
|
+
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
103
|
+
|
|
104
|
+
# LLM specific (for GENERATION type)
|
|
105
|
+
model: Optional[str] = None
|
|
106
|
+
llm_parameters: Optional[Dict[str, Any]] = None
|
|
107
|
+
prompt_tokens: Optional[int] = None
|
|
108
|
+
completion_tokens: Optional[int] = None
|
|
109
|
+
total_tokens: Optional[int] = None
|
|
110
|
+
|
|
111
|
+
# Scores attached to this observation
|
|
112
|
+
scores: List[Score] = Field(default_factory=list)
|
|
113
|
+
|
|
114
|
+
def duration_ms(self) -> Optional[float]:
|
|
115
|
+
"""Calculate duration in milliseconds."""
|
|
116
|
+
if self.start_time and self.end_time:
|
|
117
|
+
delta = self.end_time - self.start_time
|
|
118
|
+
return delta.total_seconds() * 1000
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class Trace(BaseModel):
|
|
123
|
+
"""Root trace representing a complete execution flow."""
|
|
124
|
+
|
|
125
|
+
id: UUID = Field(default_factory=uuid4)
|
|
126
|
+
session_id: Optional[UUID] = None
|
|
127
|
+
name: str
|
|
128
|
+
|
|
129
|
+
# Timing
|
|
130
|
+
start_time: datetime = Field(default_factory=datetime.utcnow)
|
|
131
|
+
end_time: Optional[datetime] = None
|
|
132
|
+
|
|
133
|
+
# Status
|
|
134
|
+
status: TraceStatus = TraceStatus.PENDING
|
|
135
|
+
error: Optional[str] = None
|
|
136
|
+
|
|
137
|
+
# Context
|
|
138
|
+
user_id: Optional[str] = None
|
|
139
|
+
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
140
|
+
tags: List[str] = Field(default_factory=list)
|
|
141
|
+
|
|
142
|
+
# Experiment specific
|
|
143
|
+
experiment_id: Optional[str] = None
|
|
144
|
+
iteration: Optional[int] = None
|
|
145
|
+
persona: Optional[str] = None
|
|
146
|
+
variation_seed: Optional[str] = None
|
|
147
|
+
|
|
148
|
+
# Input/Output
|
|
149
|
+
input: Optional[Any] = None
|
|
150
|
+
output: Optional[Any] = None
|
|
151
|
+
|
|
152
|
+
# Hierarchical data
|
|
153
|
+
observations: List[Observation] = Field(default_factory=list)
|
|
154
|
+
scores: List[Score] = Field(default_factory=list)
|
|
155
|
+
|
|
156
|
+
# Metrics
|
|
157
|
+
total_cost: Optional[float] = None
|
|
158
|
+
prompt_tokens: Optional[int] = None
|
|
159
|
+
completion_tokens: Optional[int] = None
|
|
160
|
+
total_tokens: Optional[int] = None
|
|
161
|
+
|
|
162
|
+
def duration_ms(self) -> Optional[float]:
|
|
163
|
+
"""Calculate total duration in milliseconds."""
|
|
164
|
+
if self.start_time and self.end_time:
|
|
165
|
+
delta = self.end_time - self.start_time
|
|
166
|
+
return delta.total_seconds() * 1000
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
def get_observation_tree(self) -> Dict[UUID, List[Observation]]:
|
|
170
|
+
"""Build parent-child relationship tree of observations."""
|
|
171
|
+
tree: Dict[UUID, List[Observation]] = {None: []}
|
|
172
|
+
|
|
173
|
+
for obs in self.observations:
|
|
174
|
+
parent_id = obs.parent_observation_id
|
|
175
|
+
if parent_id not in tree:
|
|
176
|
+
tree[parent_id] = []
|
|
177
|
+
tree[parent_id].append(obs)
|
|
178
|
+
|
|
179
|
+
return tree
|
|
180
|
+
|
|
181
|
+
def calculate_metrics(self) -> Dict[str, Any]:
|
|
182
|
+
"""Calculate aggregate metrics for the trace."""
|
|
183
|
+
metrics = {
|
|
184
|
+
"duration_ms": self.duration_ms(),
|
|
185
|
+
"observation_count": len(self.observations),
|
|
186
|
+
"score_count": len(self.scores),
|
|
187
|
+
"error_count": sum(1 for o in self.observations if o.error),
|
|
188
|
+
"total_tokens": self.total_tokens or 0,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
# Calculate success rate from scores
|
|
192
|
+
success_scores = [s for s in self.scores if s.name == "success" and s.data_type == ScoreDataType.BOOLEAN]
|
|
193
|
+
if success_scores:
|
|
194
|
+
success_rate = sum(1 for s in success_scores if s.value) / len(success_scores)
|
|
195
|
+
metrics["success_rate"] = success_rate
|
|
196
|
+
|
|
197
|
+
return metrics
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Utilities for serializing trace and observation data."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
from uuid import UUID
|
|
9
|
+
|
|
10
|
+
from .models import ObservationData, TraceData
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _convert_uuid(value: UUID | None) -> str | None:
|
|
14
|
+
if value is None:
|
|
15
|
+
return None
|
|
16
|
+
return str(value)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _convert_datetime(value: datetime | None) -> str | None:
|
|
20
|
+
if value is None:
|
|
21
|
+
return None
|
|
22
|
+
# Use ISO 8601 format with UTC indicator when possible
|
|
23
|
+
iso = value.isoformat()
|
|
24
|
+
if value.tzinfo is None:
|
|
25
|
+
return iso + "Z"
|
|
26
|
+
return iso
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _make_json_safe(value: Any) -> Any:
|
|
30
|
+
"""Recursively convert values so they can be JSON-serialized."""
|
|
31
|
+
|
|
32
|
+
if isinstance(value, datetime):
|
|
33
|
+
return _convert_datetime(value)
|
|
34
|
+
|
|
35
|
+
if isinstance(value, UUID):
|
|
36
|
+
return str(value)
|
|
37
|
+
|
|
38
|
+
if isinstance(value, Enum):
|
|
39
|
+
return value.value
|
|
40
|
+
|
|
41
|
+
if isinstance(value, dict):
|
|
42
|
+
return {str(key): _make_json_safe(val) for key, val in value.items()}
|
|
43
|
+
|
|
44
|
+
if isinstance(value, (list, tuple, set, frozenset)):
|
|
45
|
+
return [
|
|
46
|
+
_make_json_safe(item)
|
|
47
|
+
for item in value
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
if isinstance(value, bytes):
|
|
51
|
+
return value.decode("utf-8", errors="replace")
|
|
52
|
+
|
|
53
|
+
if isinstance(value, (str, int, float, bool)) or value is None:
|
|
54
|
+
return value
|
|
55
|
+
|
|
56
|
+
return repr(value)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def serialize_trace(trace: TraceData) -> Dict[str, Any]:
|
|
60
|
+
"""Convert TraceData into a JSON-serializable dictionary."""
|
|
61
|
+
|
|
62
|
+
data = trace.model_dump(exclude_none=True)
|
|
63
|
+
|
|
64
|
+
if trace.id:
|
|
65
|
+
data["id"] = _convert_uuid(trace.id)
|
|
66
|
+
if trace.session_id:
|
|
67
|
+
data["session_id"] = _convert_uuid(trace.session_id)
|
|
68
|
+
|
|
69
|
+
if trace.start_time:
|
|
70
|
+
data["start_time"] = _convert_datetime(trace.start_time)
|
|
71
|
+
if trace.end_time:
|
|
72
|
+
data["end_time"] = _convert_datetime(trace.end_time)
|
|
73
|
+
|
|
74
|
+
if "metadata" in data:
|
|
75
|
+
data["metadata"] = _make_json_safe(data["metadata"])
|
|
76
|
+
|
|
77
|
+
if "input" in data:
|
|
78
|
+
data["input"] = _make_json_safe(data["input"])
|
|
79
|
+
|
|
80
|
+
if "output" in data:
|
|
81
|
+
data["output"] = _make_json_safe(data["output"])
|
|
82
|
+
|
|
83
|
+
return data
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def serialize_observation(observation: ObservationData) -> Dict[str, Any]:
|
|
87
|
+
"""Convert ObservationData into a JSON-serializable dictionary."""
|
|
88
|
+
|
|
89
|
+
data = observation.model_dump(exclude_none=True)
|
|
90
|
+
|
|
91
|
+
if observation.id:
|
|
92
|
+
data["id"] = _convert_uuid(observation.id)
|
|
93
|
+
if observation.trace_id:
|
|
94
|
+
data["trace_id"] = _convert_uuid(observation.trace_id)
|
|
95
|
+
if observation.parent_observation_id:
|
|
96
|
+
data["parent_observation_id"] = _convert_uuid(observation.parent_observation_id)
|
|
97
|
+
|
|
98
|
+
if observation.start_time:
|
|
99
|
+
data["start_time"] = _convert_datetime(observation.start_time)
|
|
100
|
+
if observation.end_time:
|
|
101
|
+
data["end_time"] = _convert_datetime(observation.end_time)
|
|
102
|
+
|
|
103
|
+
if "metadata" in data:
|
|
104
|
+
data["metadata"] = _make_json_safe(data["metadata"])
|
|
105
|
+
|
|
106
|
+
if "input" in data:
|
|
107
|
+
data["input"] = _make_json_safe(data["input"])
|
|
108
|
+
|
|
109
|
+
if "output" in data:
|
|
110
|
+
data["output"] = _make_json_safe(data["output"])
|
|
111
|
+
|
|
112
|
+
if "llm_parameters" in data:
|
|
113
|
+
data["llm_parameters"] = _make_json_safe(data["llm_parameters"])
|
|
114
|
+
|
|
115
|
+
return data
|
|
116
|
+
|
fluxloop/storage.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Offline storage helper for FluxLoop traces and observations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Iterable, Tuple
|
|
9
|
+
from uuid import UUID
|
|
10
|
+
|
|
11
|
+
from .config import get_config
|
|
12
|
+
from .models import ObservationData, TraceData
|
|
13
|
+
from .serialization import serialize_observation, serialize_trace
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class OfflineStore:
|
|
17
|
+
"""Persist traces and observations to local JSON artifacts."""
|
|
18
|
+
|
|
19
|
+
def __init__(self) -> None:
|
|
20
|
+
self.config = get_config()
|
|
21
|
+
self.base_dir = Path(self.config.offline_store_dir)
|
|
22
|
+
self.traces_file = self.base_dir / "traces.jsonl"
|
|
23
|
+
self.observations_file = self.base_dir / "observations.jsonl"
|
|
24
|
+
|
|
25
|
+
if self.config.offline_store_enabled:
|
|
26
|
+
self.base_dir.mkdir(parents=True, exist_ok=True)
|
|
27
|
+
|
|
28
|
+
def record_traces(self, traces: Iterable[TraceData]) -> None:
|
|
29
|
+
if not self.config.offline_store_enabled:
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
if not self.traces_file.exists():
|
|
33
|
+
self.traces_file.write_text("")
|
|
34
|
+
|
|
35
|
+
with self.traces_file.open("a") as fp:
|
|
36
|
+
for trace in traces:
|
|
37
|
+
fp.write(json.dumps(serialize_trace(trace)) + os.linesep)
|
|
38
|
+
|
|
39
|
+
def record_observations(
|
|
40
|
+
self, items: Iterable[Tuple[UUID, ObservationData]]
|
|
41
|
+
) -> None:
|
|
42
|
+
if not self.config.offline_store_enabled:
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
if not self.observations_file.exists():
|
|
46
|
+
self.observations_file.write_text("")
|
|
47
|
+
|
|
48
|
+
with self.observations_file.open("a") as fp:
|
|
49
|
+
for trace_id, observation in items:
|
|
50
|
+
payload = serialize_observation(observation)
|
|
51
|
+
payload["trace_id"] = str(trace_id)
|
|
52
|
+
fp.write(json.dumps(payload) + os.linesep)
|
|
53
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fluxloop
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: FluxLoop SDK for agent instrumentation and tracing
|
|
5
|
+
Author-email: FluxLoop Team <team@fluxloop.dev>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/fluxloop/fluxloop
|
|
8
|
+
Project-URL: Documentation, https://docs.fluxloop.dev
|
|
9
|
+
Project-URL: Repository, https://github.com/fluxloop/fluxloop
|
|
10
|
+
Project-URL: Issues, https://github.com/fluxloop/fluxloop/issues
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: pydantic>=2.0
|
|
23
|
+
Requires-Dist: httpx>=0.24.0
|
|
24
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
31
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
32
|
+
Provides-Extra: langchain
|
|
33
|
+
Requires-Dist: langchain>=0.1.0; extra == "langchain"
|
|
34
|
+
Provides-Extra: langgraph
|
|
35
|
+
Requires-Dist: langgraph>=0.0.20; extra == "langgraph"
|
|
36
|
+
|
|
37
|
+
# FluxLoop SDK
|
|
38
|
+
|
|
39
|
+
FluxLoop SDK for agent instrumentation and tracing.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install fluxloop
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from fluxloop import trace, FluxLoopClient
|
|
51
|
+
|
|
52
|
+
# Initialize the client
|
|
53
|
+
client = FluxLoopClient()
|
|
54
|
+
|
|
55
|
+
# Use the trace decorator
|
|
56
|
+
@trace()
|
|
57
|
+
def my_agent_function(prompt: str):
|
|
58
|
+
# Your agent logic here
|
|
59
|
+
return result
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Features
|
|
63
|
+
|
|
64
|
+
- 🔍 **Automatic Tracing**: Instrument your agent code with simple decorators
|
|
65
|
+
- 📊 **Rich Context**: Capture inputs, outputs, and metadata
|
|
66
|
+
- 🔄 **Async Support**: Works with both sync and async functions
|
|
67
|
+
- 🎯 **Framework Integration**: Built-in support for LangChain and LangGraph
|
|
68
|
+
|
|
69
|
+
## Documentation
|
|
70
|
+
|
|
71
|
+
For detailed documentation, visit [https://docs.fluxloop.dev](https://docs.fluxloop.dev)
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
Apache License 2.0 - see LICENSE file for details
|
|
76
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
fluxloop/__init__.py,sha256=TomDcCLgeFB1LG3NpspyFVQ3bd5HNr4CPA2etl5XXKs,1248
|
|
2
|
+
fluxloop/buffer.py,sha256=pbviGFU0P_HlBYOlZccVuaj7OcQ5-M0L8d7r_vF1hQg,5940
|
|
3
|
+
fluxloop/client.py,sha256=4WCi1de8MJW-5La-ZalVYEaN7-gUFHlr6mByi1bBoc0,5487
|
|
4
|
+
fluxloop/config.py,sha256=A7hekvUFpQCeAcOEqj7d4fW8dvPihfaQ51ZbamiOyGw,5723
|
|
5
|
+
fluxloop/context.py,sha256=09VTTSnn72E0wRf55PX5j355ngKE2GXaqCYpgBVgxMM,6458
|
|
6
|
+
fluxloop/decorators.py,sha256=nfKIQpLwfMhr7A1TjtL-vppPWroPs98LsFB_1-ADfFM,15519
|
|
7
|
+
fluxloop/models.py,sha256=5AQ2jqArRGwQB91GOBVrBRb1h5gJBghl5G81EClOUp8,2399
|
|
8
|
+
fluxloop/recording.py,sha256=lnhJI0fWJfgMGMjw6TA8wU66PbelfYyD2UzMJ02W-uI,6609
|
|
9
|
+
fluxloop/serialization.py,sha256=SkY4bSUHxBrCkrPns-jWC1PkVSX8qh-oBprQTbmc5Z8,3239
|
|
10
|
+
fluxloop/storage.py,sha256=siwlbGdUpFAzdI6MMnODtkuxJyftQG9D4Phs2s5quMs,1732
|
|
11
|
+
fluxloop/schemas/__init__.py,sha256=przYFHTxNK7juJbqWZ2YVuCdBiMk3PB1H7T4xg3l62g,911
|
|
12
|
+
fluxloop/schemas/config.py,sha256=ib7GZVfWgUh9flwix9oKEOijyG0lWZjNBxC9lLbKZDw,11120
|
|
13
|
+
fluxloop/schemas/trace.py,sha256=tkeYQon9pCTZLp1Ad3e-KM86xLaofIqxjHJ7l-mX0Bk,6270
|
|
14
|
+
fluxloop-0.1.0.dist-info/METADATA,sha256=zeO_FZNKSlYYKAo-QDHZq2UJcbVqwWzGGfQ8ZSHWItM,2320
|
|
15
|
+
fluxloop-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
+
fluxloop-0.1.0.dist-info/top_level.txt,sha256=-whiUKvhn6Y7-TLfqrY7fZ1Cudjk8PnrKe7h7m8cuL4,9
|
|
17
|
+
fluxloop-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fluxloop
|