agent-first-data 0.1.3__tar.gz → 0.2.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.
- agent_first_data-0.2.0/PKG-INFO +281 -0
- agent_first_data-0.2.0/README.md +272 -0
- agent_first_data-0.2.0/agent_first_data/__init__.py +25 -0
- agent_first_data-0.2.0/agent_first_data/format.py +509 -0
- agent_first_data-0.2.0/agent_first_data.egg-info/PKG-INFO +281 -0
- {agent_first_data-0.1.3 → agent_first_data-0.2.0}/agent_first_data.egg-info/SOURCES.txt +1 -0
- {agent_first_data-0.1.3 → agent_first_data-0.2.0}/pyproject.toml +2 -1
- {agent_first_data-0.1.3 → agent_first_data-0.2.0}/tests/test_format.py +12 -77
- agent_first_data-0.1.3/PKG-INFO +0 -7
- agent_first_data-0.1.3/agent_first_data/__init__.py +0 -29
- agent_first_data-0.1.3/agent_first_data/format.py +0 -352
- agent_first_data-0.1.3/agent_first_data.egg-info/PKG-INFO +0 -7
- {agent_first_data-0.1.3 → agent_first_data-0.2.0}/agent_first_data.egg-info/dependency_links.txt +0 -0
- {agent_first_data-0.1.3 → agent_first_data-0.2.0}/agent_first_data.egg-info/top_level.txt +0 -0
- {agent_first_data-0.1.3 → agent_first_data-0.2.0}/setup.cfg +0 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-first-data
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Agent-First Data (AFD) — suffix-driven output formatting and protocol templates for AI agents
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/cmnspore/agent-first-data
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# agent-first-data
|
|
11
|
+
|
|
12
|
+
**Agent-First Data (AFD)** — Suffix-driven output formatting and protocol templates for AI agents.
|
|
13
|
+
|
|
14
|
+
The field name is the schema. Agents read `latency_ms` and know milliseconds, `api_key_secret` and know to redact, no external schema needed.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install agent-first-data
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## API Reference
|
|
23
|
+
|
|
24
|
+
Total: **9 public APIs** (4 protocol builders + 3 output functions + 1 internal + 1 utility)
|
|
25
|
+
|
|
26
|
+
### Protocol Builders (returns dict)
|
|
27
|
+
|
|
28
|
+
Build AFD protocol structures. Return dict objects for API responses.
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
# Startup (configuration)
|
|
32
|
+
build_json_startup(config: Any, args: Any, env: Any) -> dict
|
|
33
|
+
|
|
34
|
+
# Success (result)
|
|
35
|
+
build_json_ok(result: Any, trace: Any = None) -> dict
|
|
36
|
+
|
|
37
|
+
# Error (simple message)
|
|
38
|
+
build_json_error(message: str, trace: Any = None) -> dict
|
|
39
|
+
|
|
40
|
+
# Generic (any code + fields)
|
|
41
|
+
build_json(code: str, fields: Any, trace: Any = None) -> dict
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Use case:** API responses (frameworks like FastAPI automatically serialize)
|
|
45
|
+
|
|
46
|
+
**Example:**
|
|
47
|
+
```python
|
|
48
|
+
from agent_first_data import *
|
|
49
|
+
|
|
50
|
+
# Startup
|
|
51
|
+
startup = build_json_startup(
|
|
52
|
+
{"api_key_secret": "sk-123", "timeout_s": 30},
|
|
53
|
+
{"config_path": "config.yml"},
|
|
54
|
+
{"RUST_LOG": "info"},
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Success (always include trace)
|
|
58
|
+
response = build_json_ok(
|
|
59
|
+
{"user_id": 123},
|
|
60
|
+
trace={"duration_ms": 150, "source": "db"},
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Error
|
|
64
|
+
err = build_json_error("user not found", trace={"duration_ms": 5})
|
|
65
|
+
|
|
66
|
+
# Specific error code
|
|
67
|
+
not_found = build_json(
|
|
68
|
+
"not_found",
|
|
69
|
+
{"resource": "user", "id": 123},
|
|
70
|
+
trace={"duration_ms": 8},
|
|
71
|
+
)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### CLI/Log Output (returns str)
|
|
75
|
+
|
|
76
|
+
Format values for CLI output and logs. **All formats redact `_secret` fields.** YAML and Plain also strip suffixes from keys and format values for human readability.
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
output_json(value: Any) -> str # Single-line JSON, original keys, for programs/logs
|
|
80
|
+
output_yaml(value: Any) -> str # Multi-line YAML, keys stripped, values formatted
|
|
81
|
+
output_plain(value: Any) -> str # Single-line logfmt, keys stripped, values formatted
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Example:**
|
|
85
|
+
```python
|
|
86
|
+
from agent_first_data import *
|
|
87
|
+
|
|
88
|
+
data = {
|
|
89
|
+
"user_id": 123,
|
|
90
|
+
"api_key_secret": "sk-1234567890abcdef",
|
|
91
|
+
"created_at_epoch_ms": 1738886400000,
|
|
92
|
+
"file_size_bytes": 5242880,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# JSON (secrets redacted, original keys, raw values)
|
|
96
|
+
print(output_json(data))
|
|
97
|
+
# {"api_key_secret":"***","created_at_epoch_ms":1738886400000,"file_size_bytes":5242880,"user_id":123}
|
|
98
|
+
|
|
99
|
+
# YAML (keys stripped, values formatted, secrets redacted)
|
|
100
|
+
print(output_yaml(data))
|
|
101
|
+
# ---
|
|
102
|
+
# api_key: "***"
|
|
103
|
+
# created_at: "2025-02-07T00:00:00.000Z"
|
|
104
|
+
# file_size: "5.0MB"
|
|
105
|
+
# user_id: 123
|
|
106
|
+
|
|
107
|
+
# Plain logfmt (keys stripped, values formatted, secrets redacted)
|
|
108
|
+
print(output_plain(data))
|
|
109
|
+
# api_key=*** created_at=2025-02-07T00:00:00.000Z file_size=5.0MB user_id=123
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Internal Tools
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
internal_redact_secrets(value: Any) -> None # Manually redact secrets in-place
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Most users don't need this. Output functions automatically protect secrets.
|
|
119
|
+
|
|
120
|
+
### Utility Functions
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
parse_size(s: str) -> int | None # Parse "10M" → bytes
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Example:**
|
|
127
|
+
```python
|
|
128
|
+
from agent_first_data import *
|
|
129
|
+
|
|
130
|
+
assert parse_size("10M") == 10485760
|
|
131
|
+
assert parse_size("1.5K") == 1536
|
|
132
|
+
assert parse_size("512") == 512
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Usage Examples
|
|
136
|
+
|
|
137
|
+
### Example 1: REST API
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from agent_first_data import *
|
|
141
|
+
from fastapi import FastAPI
|
|
142
|
+
|
|
143
|
+
app = FastAPI()
|
|
144
|
+
|
|
145
|
+
@app.get("/users/{user_id}")
|
|
146
|
+
async def get_user(user_id: int):
|
|
147
|
+
response = build_json_ok(
|
|
148
|
+
{"user_id": user_id, "name": "alice"},
|
|
149
|
+
trace={"duration_ms": 150, "source": "db"},
|
|
150
|
+
)
|
|
151
|
+
# API returns raw JSON — no output processing, no key stripping
|
|
152
|
+
return response
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Example 2: CLI Tool (Complete Lifecycle)
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from agent_first_data import *
|
|
159
|
+
|
|
160
|
+
# 1. Startup
|
|
161
|
+
startup = build_json_startup(
|
|
162
|
+
{"api_key_secret": "sk-sensitive-key", "timeout_s": 30},
|
|
163
|
+
{"input_path": "data.json"},
|
|
164
|
+
{"RUST_LOG": "info"},
|
|
165
|
+
)
|
|
166
|
+
print(output_yaml(startup))
|
|
167
|
+
# ---
|
|
168
|
+
# code: "startup"
|
|
169
|
+
# args:
|
|
170
|
+
# input_path: "data.json"
|
|
171
|
+
# config:
|
|
172
|
+
# api_key: "***"
|
|
173
|
+
# timeout: "30s"
|
|
174
|
+
# env:
|
|
175
|
+
# RUST_LOG: "info"
|
|
176
|
+
|
|
177
|
+
# 2. Progress
|
|
178
|
+
progress = build_json(
|
|
179
|
+
"progress",
|
|
180
|
+
{"current": 3, "total": 10, "message": "processing"},
|
|
181
|
+
trace={"duration_ms": 1500},
|
|
182
|
+
)
|
|
183
|
+
print(output_plain(progress))
|
|
184
|
+
# code=progress current=3 message=processing total=10 trace.duration=1.5s
|
|
185
|
+
|
|
186
|
+
# 3. Result
|
|
187
|
+
result = build_json_ok(
|
|
188
|
+
{
|
|
189
|
+
"records_processed": 10,
|
|
190
|
+
"file_size_bytes": 5242880,
|
|
191
|
+
"created_at_epoch_ms": 1738886400000,
|
|
192
|
+
},
|
|
193
|
+
trace={"duration_ms": 3500, "source": "file"},
|
|
194
|
+
)
|
|
195
|
+
print(output_yaml(result))
|
|
196
|
+
# ---
|
|
197
|
+
# code: "ok"
|
|
198
|
+
# result:
|
|
199
|
+
# created_at: "2025-02-07T00:00:00.000Z"
|
|
200
|
+
# file_size: "5.0MB"
|
|
201
|
+
# records_processed: 10
|
|
202
|
+
# trace:
|
|
203
|
+
# duration: "3.5s"
|
|
204
|
+
# source: "file"
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Example 3: JSONL Output
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from agent_first_data import *
|
|
211
|
+
|
|
212
|
+
result = build_json_ok(
|
|
213
|
+
{"status": "success"},
|
|
214
|
+
trace={"duration_ms": 250, "api_key_secret": "sk-123"},
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# Print JSONL to stdout (secrets redacted, one JSON object per line)
|
|
218
|
+
print(output_json(result))
|
|
219
|
+
# {"code":"ok","result":{"status":"success"},"trace":{"api_key_secret":"***","duration_ms":250}}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Complete Suffix Example
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
from agent_first_data import *
|
|
226
|
+
|
|
227
|
+
data = {
|
|
228
|
+
"created_at_epoch_ms": 1738886400000,
|
|
229
|
+
"request_timeout_ms": 5000,
|
|
230
|
+
"cache_ttl_s": 3600,
|
|
231
|
+
"file_size_bytes": 5242880,
|
|
232
|
+
"payment_msats": 50000000,
|
|
233
|
+
"price_usd_cents": 9999,
|
|
234
|
+
"success_rate_percent": 95.5,
|
|
235
|
+
"api_key_secret": "sk-1234567890abcdef",
|
|
236
|
+
"user_name": "alice",
|
|
237
|
+
"count": 42,
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
# YAML output (keys stripped, values formatted, secrets redacted)
|
|
241
|
+
print(output_yaml(data))
|
|
242
|
+
# ---
|
|
243
|
+
# api_key: "***"
|
|
244
|
+
# cache_ttl: "3600s"
|
|
245
|
+
# count: 42
|
|
246
|
+
# created_at: "2025-02-07T00:00:00.000Z"
|
|
247
|
+
# file_size: "5.0MB"
|
|
248
|
+
# payment: "50000000msats"
|
|
249
|
+
# price: "$99.99"
|
|
250
|
+
# request_timeout: "5.0s"
|
|
251
|
+
# success_rate: "95.5%"
|
|
252
|
+
# user_name: "alice"
|
|
253
|
+
|
|
254
|
+
# Plain logfmt output (same transformations, single line)
|
|
255
|
+
print(output_plain(data))
|
|
256
|
+
# api_key=*** cache_ttl=3600s count=42 created_at=2025-02-07T00:00:00.000Z file_size=5.0MB payment=50000000msats price=$99.99 request_timeout=5.0s success_rate=95.5% user_name=alice
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Output Formats
|
|
260
|
+
|
|
261
|
+
Three output formats for different use cases:
|
|
262
|
+
|
|
263
|
+
| Format | Structure | Keys | Values | Use case |
|
|
264
|
+
|:-------|:----------|:-----|:-------|:---------|
|
|
265
|
+
| **JSON** | single-line | original (with suffix) | raw | programs, logs |
|
|
266
|
+
| **YAML** | multi-line | stripped | formatted | human inspection |
|
|
267
|
+
| **Plain** | single-line logfmt | stripped | formatted | compact scanning |
|
|
268
|
+
|
|
269
|
+
All formats automatically redact `_secret` fields.
|
|
270
|
+
|
|
271
|
+
## Supported Suffixes
|
|
272
|
+
|
|
273
|
+
- **Duration**: `_ms`, `_s`, `_ns`, `_us`, `_minutes`, `_hours`, `_days`
|
|
274
|
+
- **Timestamps**: `_epoch_ms`, `_epoch_s`, `_epoch_ns`, `_rfc3339`
|
|
275
|
+
- **Size**: `_bytes` (auto-scales to KB/MB/GB/TB), `_size` (config input, pass through)
|
|
276
|
+
- **Currency**: `_msats`, `_sats`, `_btc`, `_usd_cents`, `_eur_cents`, `_jpy`, `_{code}_cents`
|
|
277
|
+
- **Other**: `_percent`, `_secret` (auto-redacted in all formats)
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
MIT
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# agent-first-data
|
|
2
|
+
|
|
3
|
+
**Agent-First Data (AFD)** — Suffix-driven output formatting and protocol templates for AI agents.
|
|
4
|
+
|
|
5
|
+
The field name is the schema. Agents read `latency_ms` and know milliseconds, `api_key_secret` and know to redact, no external schema needed.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install agent-first-data
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## API Reference
|
|
14
|
+
|
|
15
|
+
Total: **9 public APIs** (4 protocol builders + 3 output functions + 1 internal + 1 utility)
|
|
16
|
+
|
|
17
|
+
### Protocol Builders (returns dict)
|
|
18
|
+
|
|
19
|
+
Build AFD protocol structures. Return dict objects for API responses.
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
# Startup (configuration)
|
|
23
|
+
build_json_startup(config: Any, args: Any, env: Any) -> dict
|
|
24
|
+
|
|
25
|
+
# Success (result)
|
|
26
|
+
build_json_ok(result: Any, trace: Any = None) -> dict
|
|
27
|
+
|
|
28
|
+
# Error (simple message)
|
|
29
|
+
build_json_error(message: str, trace: Any = None) -> dict
|
|
30
|
+
|
|
31
|
+
# Generic (any code + fields)
|
|
32
|
+
build_json(code: str, fields: Any, trace: Any = None) -> dict
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Use case:** API responses (frameworks like FastAPI automatically serialize)
|
|
36
|
+
|
|
37
|
+
**Example:**
|
|
38
|
+
```python
|
|
39
|
+
from agent_first_data import *
|
|
40
|
+
|
|
41
|
+
# Startup
|
|
42
|
+
startup = build_json_startup(
|
|
43
|
+
{"api_key_secret": "sk-123", "timeout_s": 30},
|
|
44
|
+
{"config_path": "config.yml"},
|
|
45
|
+
{"RUST_LOG": "info"},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Success (always include trace)
|
|
49
|
+
response = build_json_ok(
|
|
50
|
+
{"user_id": 123},
|
|
51
|
+
trace={"duration_ms": 150, "source": "db"},
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Error
|
|
55
|
+
err = build_json_error("user not found", trace={"duration_ms": 5})
|
|
56
|
+
|
|
57
|
+
# Specific error code
|
|
58
|
+
not_found = build_json(
|
|
59
|
+
"not_found",
|
|
60
|
+
{"resource": "user", "id": 123},
|
|
61
|
+
trace={"duration_ms": 8},
|
|
62
|
+
)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### CLI/Log Output (returns str)
|
|
66
|
+
|
|
67
|
+
Format values for CLI output and logs. **All formats redact `_secret` fields.** YAML and Plain also strip suffixes from keys and format values for human readability.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
output_json(value: Any) -> str # Single-line JSON, original keys, for programs/logs
|
|
71
|
+
output_yaml(value: Any) -> str # Multi-line YAML, keys stripped, values formatted
|
|
72
|
+
output_plain(value: Any) -> str # Single-line logfmt, keys stripped, values formatted
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Example:**
|
|
76
|
+
```python
|
|
77
|
+
from agent_first_data import *
|
|
78
|
+
|
|
79
|
+
data = {
|
|
80
|
+
"user_id": 123,
|
|
81
|
+
"api_key_secret": "sk-1234567890abcdef",
|
|
82
|
+
"created_at_epoch_ms": 1738886400000,
|
|
83
|
+
"file_size_bytes": 5242880,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# JSON (secrets redacted, original keys, raw values)
|
|
87
|
+
print(output_json(data))
|
|
88
|
+
# {"api_key_secret":"***","created_at_epoch_ms":1738886400000,"file_size_bytes":5242880,"user_id":123}
|
|
89
|
+
|
|
90
|
+
# YAML (keys stripped, values formatted, secrets redacted)
|
|
91
|
+
print(output_yaml(data))
|
|
92
|
+
# ---
|
|
93
|
+
# api_key: "***"
|
|
94
|
+
# created_at: "2025-02-07T00:00:00.000Z"
|
|
95
|
+
# file_size: "5.0MB"
|
|
96
|
+
# user_id: 123
|
|
97
|
+
|
|
98
|
+
# Plain logfmt (keys stripped, values formatted, secrets redacted)
|
|
99
|
+
print(output_plain(data))
|
|
100
|
+
# api_key=*** created_at=2025-02-07T00:00:00.000Z file_size=5.0MB user_id=123
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Internal Tools
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
internal_redact_secrets(value: Any) -> None # Manually redact secrets in-place
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Most users don't need this. Output functions automatically protect secrets.
|
|
110
|
+
|
|
111
|
+
### Utility Functions
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
parse_size(s: str) -> int | None # Parse "10M" → bytes
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Example:**
|
|
118
|
+
```python
|
|
119
|
+
from agent_first_data import *
|
|
120
|
+
|
|
121
|
+
assert parse_size("10M") == 10485760
|
|
122
|
+
assert parse_size("1.5K") == 1536
|
|
123
|
+
assert parse_size("512") == 512
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Usage Examples
|
|
127
|
+
|
|
128
|
+
### Example 1: REST API
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from agent_first_data import *
|
|
132
|
+
from fastapi import FastAPI
|
|
133
|
+
|
|
134
|
+
app = FastAPI()
|
|
135
|
+
|
|
136
|
+
@app.get("/users/{user_id}")
|
|
137
|
+
async def get_user(user_id: int):
|
|
138
|
+
response = build_json_ok(
|
|
139
|
+
{"user_id": user_id, "name": "alice"},
|
|
140
|
+
trace={"duration_ms": 150, "source": "db"},
|
|
141
|
+
)
|
|
142
|
+
# API returns raw JSON — no output processing, no key stripping
|
|
143
|
+
return response
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Example 2: CLI Tool (Complete Lifecycle)
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from agent_first_data import *
|
|
150
|
+
|
|
151
|
+
# 1. Startup
|
|
152
|
+
startup = build_json_startup(
|
|
153
|
+
{"api_key_secret": "sk-sensitive-key", "timeout_s": 30},
|
|
154
|
+
{"input_path": "data.json"},
|
|
155
|
+
{"RUST_LOG": "info"},
|
|
156
|
+
)
|
|
157
|
+
print(output_yaml(startup))
|
|
158
|
+
# ---
|
|
159
|
+
# code: "startup"
|
|
160
|
+
# args:
|
|
161
|
+
# input_path: "data.json"
|
|
162
|
+
# config:
|
|
163
|
+
# api_key: "***"
|
|
164
|
+
# timeout: "30s"
|
|
165
|
+
# env:
|
|
166
|
+
# RUST_LOG: "info"
|
|
167
|
+
|
|
168
|
+
# 2. Progress
|
|
169
|
+
progress = build_json(
|
|
170
|
+
"progress",
|
|
171
|
+
{"current": 3, "total": 10, "message": "processing"},
|
|
172
|
+
trace={"duration_ms": 1500},
|
|
173
|
+
)
|
|
174
|
+
print(output_plain(progress))
|
|
175
|
+
# code=progress current=3 message=processing total=10 trace.duration=1.5s
|
|
176
|
+
|
|
177
|
+
# 3. Result
|
|
178
|
+
result = build_json_ok(
|
|
179
|
+
{
|
|
180
|
+
"records_processed": 10,
|
|
181
|
+
"file_size_bytes": 5242880,
|
|
182
|
+
"created_at_epoch_ms": 1738886400000,
|
|
183
|
+
},
|
|
184
|
+
trace={"duration_ms": 3500, "source": "file"},
|
|
185
|
+
)
|
|
186
|
+
print(output_yaml(result))
|
|
187
|
+
# ---
|
|
188
|
+
# code: "ok"
|
|
189
|
+
# result:
|
|
190
|
+
# created_at: "2025-02-07T00:00:00.000Z"
|
|
191
|
+
# file_size: "5.0MB"
|
|
192
|
+
# records_processed: 10
|
|
193
|
+
# trace:
|
|
194
|
+
# duration: "3.5s"
|
|
195
|
+
# source: "file"
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Example 3: JSONL Output
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from agent_first_data import *
|
|
202
|
+
|
|
203
|
+
result = build_json_ok(
|
|
204
|
+
{"status": "success"},
|
|
205
|
+
trace={"duration_ms": 250, "api_key_secret": "sk-123"},
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Print JSONL to stdout (secrets redacted, one JSON object per line)
|
|
209
|
+
print(output_json(result))
|
|
210
|
+
# {"code":"ok","result":{"status":"success"},"trace":{"api_key_secret":"***","duration_ms":250}}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Complete Suffix Example
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from agent_first_data import *
|
|
217
|
+
|
|
218
|
+
data = {
|
|
219
|
+
"created_at_epoch_ms": 1738886400000,
|
|
220
|
+
"request_timeout_ms": 5000,
|
|
221
|
+
"cache_ttl_s": 3600,
|
|
222
|
+
"file_size_bytes": 5242880,
|
|
223
|
+
"payment_msats": 50000000,
|
|
224
|
+
"price_usd_cents": 9999,
|
|
225
|
+
"success_rate_percent": 95.5,
|
|
226
|
+
"api_key_secret": "sk-1234567890abcdef",
|
|
227
|
+
"user_name": "alice",
|
|
228
|
+
"count": 42,
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
# YAML output (keys stripped, values formatted, secrets redacted)
|
|
232
|
+
print(output_yaml(data))
|
|
233
|
+
# ---
|
|
234
|
+
# api_key: "***"
|
|
235
|
+
# cache_ttl: "3600s"
|
|
236
|
+
# count: 42
|
|
237
|
+
# created_at: "2025-02-07T00:00:00.000Z"
|
|
238
|
+
# file_size: "5.0MB"
|
|
239
|
+
# payment: "50000000msats"
|
|
240
|
+
# price: "$99.99"
|
|
241
|
+
# request_timeout: "5.0s"
|
|
242
|
+
# success_rate: "95.5%"
|
|
243
|
+
# user_name: "alice"
|
|
244
|
+
|
|
245
|
+
# Plain logfmt output (same transformations, single line)
|
|
246
|
+
print(output_plain(data))
|
|
247
|
+
# api_key=*** cache_ttl=3600s count=42 created_at=2025-02-07T00:00:00.000Z file_size=5.0MB payment=50000000msats price=$99.99 request_timeout=5.0s success_rate=95.5% user_name=alice
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Output Formats
|
|
251
|
+
|
|
252
|
+
Three output formats for different use cases:
|
|
253
|
+
|
|
254
|
+
| Format | Structure | Keys | Values | Use case |
|
|
255
|
+
|:-------|:----------|:-----|:-------|:---------|
|
|
256
|
+
| **JSON** | single-line | original (with suffix) | raw | programs, logs |
|
|
257
|
+
| **YAML** | multi-line | stripped | formatted | human inspection |
|
|
258
|
+
| **Plain** | single-line logfmt | stripped | formatted | compact scanning |
|
|
259
|
+
|
|
260
|
+
All formats automatically redact `_secret` fields.
|
|
261
|
+
|
|
262
|
+
## Supported Suffixes
|
|
263
|
+
|
|
264
|
+
- **Duration**: `_ms`, `_s`, `_ns`, `_us`, `_minutes`, `_hours`, `_days`
|
|
265
|
+
- **Timestamps**: `_epoch_ms`, `_epoch_s`, `_epoch_ns`, `_rfc3339`
|
|
266
|
+
- **Size**: `_bytes` (auto-scales to KB/MB/GB/TB), `_size` (config input, pass through)
|
|
267
|
+
- **Currency**: `_msats`, `_sats`, `_btc`, `_usd_cents`, `_eur_cents`, `_jpy`, `_{code}_cents`
|
|
268
|
+
- **Other**: `_percent`, `_secret` (auto-redacted in all formats)
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
MIT
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Agent-First Data (AFD) — suffix-driven output formatting and protocol templates."""
|
|
2
|
+
|
|
3
|
+
from agent_first_data.format import (
|
|
4
|
+
build_json_startup,
|
|
5
|
+
build_json_ok,
|
|
6
|
+
build_json_error,
|
|
7
|
+
build_json,
|
|
8
|
+
output_json,
|
|
9
|
+
output_yaml,
|
|
10
|
+
output_plain,
|
|
11
|
+
internal_redact_secrets,
|
|
12
|
+
parse_size,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"build_json_startup",
|
|
17
|
+
"build_json_ok",
|
|
18
|
+
"build_json_error",
|
|
19
|
+
"build_json",
|
|
20
|
+
"output_json",
|
|
21
|
+
"output_yaml",
|
|
22
|
+
"output_plain",
|
|
23
|
+
"internal_redact_secrets",
|
|
24
|
+
"parse_size",
|
|
25
|
+
]
|