agent-first-data 0.12.0__tar.gz → 0.12.2__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.
Files changed (18) hide show
  1. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/PKG-INFO +1 -1
  2. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/agent_first_data/__init__.py +0 -2
  3. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/agent_first_data/afdata_logging.py +0 -4
  4. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/agent_first_data/format.py +26 -6
  5. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/agent_first_data.egg-info/PKG-INFO +1 -1
  6. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/pyproject.toml +1 -1
  7. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/tests/test_afdata_logging.py +5 -5
  8. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/README.md +0 -0
  9. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/agent_first_data/cli.py +0 -0
  10. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/agent_first_data/skill.py +0 -0
  11. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/agent_first_data.egg-info/SOURCES.txt +0 -0
  12. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/agent_first_data.egg-info/dependency_links.txt +0 -0
  13. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/agent_first_data.egg-info/top_level.txt +0 -0
  14. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/setup.cfg +0 -0
  15. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/tests/test_cli.py +0 -0
  16. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/tests/test_format.py +0 -0
  17. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/tests/test_no_stderr_policy.py +0 -0
  18. {agent_first_data-0.12.0 → agent_first_data-0.12.2}/tests/test_skill.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-first-data
3
- Version: 0.12.0
3
+ Version: 0.12.2
4
4
  Summary: A naming convention that lets AI agents understand your data without being told what it means.
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/agentfirstkit/agent-first-data
@@ -27,7 +27,6 @@ from agent_first_data.format import (
27
27
 
28
28
  from agent_first_data.afdata_logging import (
29
29
  AfdataHandler,
30
- AfdataJsonHandler,
31
30
  init_json as init_logging_json,
32
31
  init_plain as init_logging_plain,
33
32
  init_yaml as init_logging_yaml,
@@ -84,7 +83,6 @@ __all__ = [
84
83
  "redact_url_secrets_with_options",
85
84
  "parse_size",
86
85
  "AfdataHandler",
87
- "AfdataJsonHandler",
88
86
  "init_logging_json",
89
87
  "init_logging_plain",
90
88
  "init_logging_yaml",
@@ -87,10 +87,6 @@ class AfdataHandler(logging.Handler):
87
87
  sys.stdout.flush()
88
88
 
89
89
 
90
- # Keep old name as alias for backwards compat
91
- AfdataJsonHandler = AfdataHandler
92
-
93
-
94
90
  class _AfdataLoggerAdapter(logging.LoggerAdapter):
95
91
  """Logger adapter that passes extra fields to AfdataHandler."""
96
92
 
@@ -11,7 +11,7 @@ from __future__ import annotations
11
11
  import json
12
12
  import math
13
13
  from dataclasses import dataclass, field
14
- from datetime import datetime, timezone
14
+ from datetime import datetime, timedelta, timezone
15
15
  from enum import Enum
16
16
  from typing import Any, Sequence
17
17
  from urllib.parse import unquote_plus
@@ -515,9 +515,27 @@ def _is_number(value: Any) -> bool:
515
515
  return isinstance(value, (int, float)) and not isinstance(value, bool)
516
516
 
517
517
 
518
+ def _number_str(value: int | float) -> str:
519
+ """Render a number canonically for YAML/plain output.
520
+
521
+ An integral-valued float drops its trailing ``.0`` (``3.0`` -> ``3``) so the
522
+ four language implementations stay byte-identical with Go/TS/Rust.
523
+ """
524
+ if isinstance(value, float) and math.isfinite(value) and value.is_integer():
525
+ return str(int(value))
526
+ return str(value)
527
+
528
+
518
529
  def _as_int(value: Any) -> int | None:
519
- if isinstance(value, int) and not isinstance(value, bool):
530
+ if isinstance(value, bool):
531
+ return None
532
+ if isinstance(value, int):
520
533
  return value
534
+ # Accept integral-valued floats (3.0 -> 3): JS/TS cannot distinguish 3 from
535
+ # 3.0 after JSON parsing, so integrality (not lexical form) gates integer
536
+ # suffixes, keeping the four implementations consistent.
537
+ if isinstance(value, float) and math.isfinite(value) and value.is_integer():
538
+ return int(value)
521
539
  return None
522
540
 
523
541
 
@@ -714,10 +732,12 @@ def _format_ms_value(value: Any) -> str | None:
714
732
 
715
733
 
716
734
  def _format_rfc3339_ms(ms: int) -> str:
735
+ # Build from an integer timedelta (not float ms/1000) so large values keep
736
+ # full precision and the second split matches Rust's div_euclid exactly.
717
737
  try:
718
- dt = datetime.fromtimestamp(ms / 1000, tz=timezone.utc)
738
+ dt = datetime(1970, 1, 1, tzinfo=timezone.utc) + timedelta(milliseconds=ms)
719
739
  return dt.strftime("%Y-%m-%dT%H:%M:%S.") + f"{ms % 1000:03d}Z"
720
- except (OSError, OverflowError, ValueError):
740
+ except (OverflowError, ValueError):
721
741
  return str(ms)
722
742
 
723
743
 
@@ -854,7 +874,7 @@ def _yaml_scalar(value: Any) -> str:
854
874
  if isinstance(value, bool):
855
875
  return "true" if value else "false"
856
876
  if isinstance(value, (int, float)):
857
- return str(value)
877
+ return _number_str(value)
858
878
  escaped = str(value).replace('"', '\\"')
859
879
  return f'"{escaped}"'
860
880
 
@@ -907,7 +927,7 @@ def _plain_scalar(value: Any) -> str:
907
927
  if isinstance(value, bool):
908
928
  return "true" if value else "false"
909
929
  if isinstance(value, (int, float)):
910
- return str(value)
930
+ return _number_str(value)
911
931
  return str(value)
912
932
 
913
933
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-first-data
3
- Version: 0.12.0
3
+ Version: 0.12.2
4
4
  Summary: A naming convention that lets AI agents understand your data without being told what it means.
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/agentfirstkit/agent-first-data
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agent-first-data"
3
- version = "0.12.0"
3
+ version = "0.12.2"
4
4
  description = "A naming convention that lets AI agents understand your data without being told what it means."
5
5
  license = "MIT"
6
6
  readme = "README.md"
@@ -6,7 +6,7 @@ import sys
6
6
  from io import StringIO
7
7
  from unittest.mock import patch
8
8
 
9
- from agent_first_data.afdata_logging import AfdataHandler, AfdataJsonHandler, init_json, init_plain, init_yaml, span, get_logger
9
+ from agent_first_data.afdata_logging import AfdataHandler, init_json, init_plain, init_yaml, span, get_logger
10
10
 
11
11
 
12
12
  def capture_log(fn):
@@ -20,9 +20,9 @@ def capture_log(fn):
20
20
 
21
21
 
22
22
  def make_logger(name="test"):
23
- """Create a fresh logger with AfdataJsonHandler."""
23
+ """Create a fresh logger with AfdataHandler."""
24
24
  logger = logging.getLogger(name)
25
- logger.handlers = [AfdataJsonHandler()]
25
+ logger.handlers = [AfdataHandler()]
26
26
  logger.setLevel(logging.DEBUG)
27
27
  return logger
28
28
 
@@ -121,9 +121,9 @@ class TestCodeOverride:
121
121
 
122
122
  class TestGetLogger:
123
123
  def test_default_fields(self):
124
- # Ensure root logger has AfdataJsonHandler
124
+ # Ensure root logger has AfdataHandler
125
125
  root = logging.getLogger()
126
- root.handlers = [AfdataJsonHandler()]
126
+ root.handlers = [AfdataHandler()]
127
127
  root.setLevel(logging.DEBUG)
128
128
 
129
129
  adapter = get_logger("test_adapter", component="myservice")