lex-llm 0.4.16 → 0.5.0

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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +13 -2
  3. data/B1b-conformance-kit.md +79 -0
  4. data/CHANGELOG.md +33 -0
  5. data/README.md +349 -153
  6. data/lex-llm.gemspec +3 -3
  7. data/lib/legion/extensions/llm/attachment.rb +1 -1
  8. data/lib/legion/extensions/llm/canonical/chunk.rb +184 -0
  9. data/lib/legion/extensions/llm/canonical/content_block.rb +126 -0
  10. data/lib/legion/extensions/llm/canonical/message.rb +125 -0
  11. data/lib/legion/extensions/llm/canonical/params.rb +61 -0
  12. data/lib/legion/extensions/llm/canonical/request.rb +117 -0
  13. data/lib/legion/extensions/llm/canonical/response.rb +124 -0
  14. data/lib/legion/extensions/llm/canonical/thinking.rb +81 -0
  15. data/lib/legion/extensions/llm/canonical/tool_call.rb +134 -0
  16. data/lib/legion/extensions/llm/canonical/tool_definition.rb +73 -0
  17. data/lib/legion/extensions/llm/canonical/usage.rb +61 -0
  18. data/lib/legion/extensions/llm/canonical.rb +49 -0
  19. data/lib/legion/extensions/llm/chat.rb +3 -5
  20. data/lib/legion/extensions/llm/connection.rb +14 -2
  21. data/lib/legion/extensions/llm/error.rb +3 -7
  22. data/lib/legion/extensions/llm/fleet/envelope_validation.rb +1 -3
  23. data/lib/legion/extensions/llm/fleet/provider_responder.rb +1 -3
  24. data/lib/legion/extensions/llm/fleet/token_validator.rb +1 -3
  25. data/lib/legion/extensions/llm/model/info.rb +4 -6
  26. data/lib/legion/extensions/llm/models.rb +3 -3
  27. data/lib/legion/extensions/llm/provider/open_ai_compatible.rb +12 -4
  28. data/lib/legion/extensions/llm/routing/lane_key.rb +1 -3
  29. data/lib/legion/extensions/llm/stream_accumulator.rb +1 -1
  30. data/lib/legion/extensions/llm/streaming.rb +6 -4
  31. data/lib/legion/extensions/llm/tool.rb +1 -3
  32. data/lib/legion/extensions/llm/version.rb +1 -1
  33. data/lib/legion/extensions/llm.rb +118 -35
  34. data/spec/fixtures/ruby.mp3 +0 -0
  35. data/spec/fixtures/ruby.mp4 +0 -0
  36. data/spec/fixtures/ruby.png +0 -0
  37. data/spec/fixtures/ruby.txt +1 -0
  38. data/spec/fixtures/ruby.wav +0 -0
  39. data/spec/fixtures/ruby.xml +1 -0
  40. data/spec/fixtures/sample.pdf +0 -0
  41. data/spec/legion/extensions/llm/agent_spec.rb +179 -0
  42. data/spec/legion/extensions/llm/attachment_spec.rb +25 -0
  43. data/spec/legion/extensions/llm/auto_registration_spec.rb +38 -0
  44. data/spec/legion/extensions/llm/canonical/chunk_spec.rb +285 -0
  45. data/spec/legion/extensions/llm/canonical/content_block_spec.rb +179 -0
  46. data/spec/legion/extensions/llm/canonical/message_spec.rb +203 -0
  47. data/spec/legion/extensions/llm/canonical/params_spec.rb +159 -0
  48. data/spec/legion/extensions/llm/canonical/request_spec.rb +174 -0
  49. data/spec/legion/extensions/llm/canonical/response_spec.rb +234 -0
  50. data/spec/legion/extensions/llm/canonical/thinking_spec.rb +151 -0
  51. data/spec/legion/extensions/llm/canonical/tool_call_spec.rb +191 -0
  52. data/spec/legion/extensions/llm/canonical/tool_definition_spec.rb +174 -0
  53. data/spec/legion/extensions/llm/canonical/usage_spec.rb +138 -0
  54. data/spec/legion/extensions/llm/configuration_spec.rb +38 -0
  55. data/spec/legion/extensions/llm/conformance/client_translator_examples.rb +269 -0
  56. data/spec/legion/extensions/llm/conformance/conformance.rb +51 -0
  57. data/spec/legion/extensions/llm/conformance/echo_translator.rb +56 -0
  58. data/spec/legion/extensions/llm/conformance/echo_translator_spec.rb +13 -0
  59. data/spec/legion/extensions/llm/conformance/fixtures/canonical_empty_response.json +13 -0
  60. data/spec/legion/extensions/llm/conformance/fixtures/canonical_error_response.json +19 -0
  61. data/spec/legion/extensions/llm/conformance/fixtures/canonical_fleet_round_trip.json +81 -0
  62. data/spec/legion/extensions/llm/conformance/fixtures/canonical_metering_audit_events.json +101 -0
  63. data/spec/legion/extensions/llm/conformance/fixtures/canonical_params_mapping_request.json +21 -0
  64. data/spec/legion/extensions/llm/conformance/fixtures/canonical_simple_text_request.json +13 -0
  65. data/spec/legion/extensions/llm/conformance/fixtures/canonical_simple_text_response.json +13 -0
  66. data/spec/legion/extensions/llm/conformance/fixtures/canonical_stop_reason_matrix.json +36 -0
  67. data/spec/legion/extensions/llm/conformance/fixtures/canonical_streaming_accumulated_response.json +20 -0
  68. data/spec/legion/extensions/llm/conformance/fixtures/canonical_streaming_error_chunks.json +26 -0
  69. data/spec/legion/extensions/llm/conformance/fixtures/canonical_streaming_text_chunks.json +33 -0
  70. data/spec/legion/extensions/llm/conformance/fixtures/canonical_streaming_thinking_chunks.json +42 -0
  71. data/spec/legion/extensions/llm/conformance/fixtures/canonical_streaming_tool_call_chunks.json +41 -0
  72. data/spec/legion/extensions/llm/conformance/fixtures/canonical_system_prompt_request.json +14 -0
  73. data/spec/legion/extensions/llm/conformance/fixtures/canonical_thinking_request.json +18 -0
  74. data/spec/legion/extensions/llm/conformance/fixtures/canonical_thinking_response.json +17 -0
  75. data/spec/legion/extensions/llm/conformance/fixtures/canonical_tool_results_continuation_request.json +75 -0
  76. data/spec/legion/extensions/llm/conformance/fixtures/canonical_tool_use_response.json +25 -0
  77. data/spec/legion/extensions/llm/conformance/fixtures/canonical_tools_request.json +34 -0
  78. data/spec/legion/extensions/llm/conformance/provider_translator_examples.rb +390 -0
  79. data/spec/legion/extensions/llm/connection_logging_spec.rb +53 -0
  80. data/spec/legion/extensions/llm/connection_retry_spec.rb +36 -0
  81. data/spec/legion/extensions/llm/context_spec.rb +127 -0
  82. data/spec/legion/extensions/llm/credential_sources_spec.rb +468 -0
  83. data/spec/legion/extensions/llm/error_middleware_spec.rb +102 -0
  84. data/spec/legion/extensions/llm/error_spec.rb +87 -0
  85. data/spec/legion/extensions/llm/fleet/provider_responder_spec.rb +120 -0
  86. data/spec/legion/extensions/llm/fleet/token_validator_spec.rb +163 -0
  87. data/spec/legion/extensions/llm/fleet/worker_execution_spec.rb +128 -0
  88. data/spec/legion/extensions/llm/fleet_messages_spec.rb +402 -0
  89. data/spec/legion/extensions/llm/gemspec_spec.rb +25 -0
  90. data/spec/legion/extensions/llm/message_spec.rb +64 -0
  91. data/spec/legion/extensions/llm/model/info_spec.rb +222 -0
  92. data/spec/legion/extensions/llm/models_spec.rb +104 -0
  93. data/spec/legion/extensions/llm/provider/open_ai_compatible_spec.rb +203 -0
  94. data/spec/legion/extensions/llm/provider_contract_spec.rb +60 -0
  95. data/spec/legion/extensions/llm/provider_settings_spec.rb +76 -0
  96. data/spec/legion/extensions/llm/provider_spec.rb +592 -0
  97. data/spec/legion/extensions/llm/registry_event_builder_spec.rb +68 -0
  98. data/spec/legion/extensions/llm/registry_publisher_spec.rb +22 -0
  99. data/spec/legion/extensions/llm/responses/response_objects_spec.rb +75 -0
  100. data/spec/legion/extensions/llm/responses/thinking_extractor_spec.rb +75 -0
  101. data/spec/legion/extensions/llm/routing/model_offering_spec.rb +222 -0
  102. data/spec/legion/extensions/llm/routing/offering_registry_spec.rb +50 -0
  103. data/spec/legion/extensions/llm/routing/registry_event_spec.rb +120 -0
  104. data/spec/legion/extensions/llm/stream_accumulator_spec.rb +103 -0
  105. data/spec/legion/extensions/llm/streaming_spec.rb +108 -0
  106. data/spec/legion/extensions/llm/tool_spec.rb +94 -0
  107. data/spec/legion/extensions/llm/transport/fleet_lane_spec.rb +60 -0
  108. data/spec/legion/extensions/llm/utils_spec.rb +113 -0
  109. data/spec/legion/extensions/llm_base_contract_spec.rb +110 -0
  110. data/spec/legion/extensions/llm_extension_spec.rb +78 -0
  111. data/spec/legion/extensions/llm_root_spec.rb +51 -0
  112. data/spec/spec_helper.rb +24 -0
  113. data/spec/support/fake_llm_provider.rb +148 -0
  114. data/spec/support/llm_configuration.rb +21 -0
  115. data/spec/support/rspec_configuration.rb +19 -0
  116. data/spec/support/simplecov_configuration.rb +20 -0
  117. metadata +110 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9bacfc210039561fd7cde72d580809804b78435a4be39c887ae5d51a9ab82d6f
4
- data.tar.gz: 46b3c44bc28c7137f6a68934779505dc3bd671630192d51f695d32fa9900c302
3
+ metadata.gz: 9ab0a51687719efbd7f048be70ff4ba57b6d81fafddfcaefb2f0d78f5f3721ae
4
+ data.tar.gz: 49e57ce1c99330d956cc92b6c6a75a283f078f5e0b8fb036ae7aad9579caccd0
5
5
  SHA512:
6
- metadata.gz: 406335de47bd45b026ffbce054cad7e7bf3f36bfb324746646064e3a75ef9d85f94987b759277ff2ecb6145969db64d3e1216c11ccc651a5fafa14bc80308fda
7
- data.tar.gz: 7b8aa303c770e39cdbff2353fb01868752aecc46fd3ea194b3e07e6207b1da3515972563f154f053d2ff8cb1d541218b1e0dec2d6d3ab458e58571399650240a
6
+ metadata.gz: 8d8fcea6f732dd4c5dfcd713024796a640941d18c8ef90f3c5b5ca0b3fcbc0094942f2cadabbdd78669ecabce8cd76f893488a92f15ec70c21ebec865fb0a531
7
+ data.tar.gz: 4a062635e491cb84b60117b1c28520a8fc5bb4d0775609ed1104639e549114c2a373e68461833008a234e33d7ac14a5fe1292cc2f37fe37b0f00818387cc4851
data/.rubocop.yml CHANGED
@@ -13,12 +13,18 @@ AllCops:
13
13
  - lib/generators/**/templates/**/*
14
14
  SuggestExtensions: false
15
15
 
16
+ Layout/LineLength:
17
+ Max: 190
16
18
  Metrics/ClassLength:
17
19
  Enabled: false
18
20
  Metrics/AbcSize:
19
- Enabled: false
21
+ Enabled: true
22
+ Max: 50
23
+ Metrics/PerceivedComplexity:
24
+ Max: 50
20
25
  Metrics/CyclomaticComplexity:
21
- Enabled: false
26
+ Enabled: true
27
+ Max: 50
22
28
  Metrics/MethodLength:
23
29
  Enabled: false
24
30
  Metrics/ModuleLength:
@@ -40,3 +46,8 @@ RSpec/MultipleExpectations:
40
46
  Enabled: false
41
47
  RSpec/LeakyLocalVariable:
42
48
  Enabled: false
49
+ # Conformance kit specs live under spec/legion/extensions/llm/conformance/
50
+ # but the test classes use Canonical::Conformance:: namespace — path can't match.
51
+ RSpec/SpecFilePathFormat:
52
+ Exclude:
53
+ - 'spec/legion/extensions/llm/conformance/**/*'
@@ -0,0 +1,79 @@
1
+ # B1b — Conformance Kit (lex-llm spec support)
2
+
3
+ > **Status:** Complete (self-test green: 54 examples, 0 failures)
4
+ > **Date:** 2026-06-10
5
+ > **Repo:** lex-llm (conformance kit only)
6
+ > **Branch:** feat/canonical-types
7
+ > **Design doc:** 2026-06-09-nxn-canonical-routing-design.md Amendment B
8
+ > **Implementation plan:** Phase 2 (conformance kit in lex-llm)
9
+ > **Dependency:** B1a canonical types (coordinator commit e1cbf820)
10
+ > **Self-test green:** `bundle exec rspec spec/legion/extensions/llm/conformance` → 54 examples, 0 failures
11
+
12
+ ---
13
+
14
+ ## What was delivered
15
+
16
+ ### Shared example groups
17
+
18
+ **provider_translator_examples.rb** (~390 lines)
19
+ `it_behaves_like 'a canonical provider translator'` — 54 scenarios across render_request,
20
+ parse_response, parse_chunk, stop_reason, round-trip.
21
+
22
+ **client_translator_examples.rb** (~270 lines)
23
+ Mirror group for client translators: `it_behaves_like 'a canonical client translator'` —
24
+ parse_request/format_request round-trip, format_response, format_chunk, format_error.
25
+
26
+ ### Fixture corpus (19 JSON files)
27
+
28
+ All under `spec/legion/extensions/llm/conformance/fixtures/`:
29
+ - simple_text request/response, system_prompt, params_mapping (all 10 G18 fields),
30
+ tools_request, tool_use_response, tool_results_continuation_request (enhanced: mixed client + registry tool calls per G4)
31
+ - canonical_thinking_request.json
32
+ - canonical_thinking_response.json (thinking content + signature, thinking_tokens)
33
+ - canonical_empty_response.json
34
+ - canonical_error_response.json
35
+ - canonical_stop_reason_matrix.json (6 canonical enums + 5 provider mappings)
36
+ - canonical_streaming_text_chunks.json
37
+ - canonical_streaming_thinking_chunks.json (thinking + text + signature + done)
38
+ - canonical_streaming_tool_call_chunks.json (multi-chunk tool-call identity per A7)
39
+ - canonical_streaming_error_chunks.json (**new**: mid-stream error per G5/G6)
40
+ - canonical_streaming_accumulated_response.json (**new**: expected assembled response from tool-call chunks)
41
+ - canonical_fleet_round_trip.json (per R6)
42
+ - canonical_metering_audit_events.json (per G15e: schemas + example events)
43
+
44
+ ### Self-test echo translator
45
+
46
+ Echo translator + spec that passes both provider and client translator groups, proving the shared examples work correctly.
47
+
48
+ ### Infrastructure fixes
49
+
50
+ 1. **lex-llm.gemspec** — spec.files now includes spec/ (was excluded); spec.require_paths adds 'spec' — enables cross-gem conformance kit loading per the amended Phase 2 spec.
51
+ 2. .rubocop.yml — excluded conformance directory from RSpec/SpecFilePathFormat.
52
+ 3. **provider_translator_examples.rb** — parse_response now tests translator.parse_response(wire) instead of canonical::Response.from_hash(fixture).
53
+
54
+ ### Issues found & fixed during this session
55
+
56
+ 1. **parse_response tests didn't exercise the translator** — Fixed by using `fixture_symbolized` + calling `translator.parse_response(wire).
57
+ 2. **Malformed rubocop directives in canonical lib files** — 5 files had `# rubocop:disable Metrics/ParameterLists, Metrics/PerceivedComplexity -- factory` where the trailing `, --` triggered `Lint/CopDirectiveSyntax`. Fixed & committed.
58
+ 3. **RSpec/SpecFilePathFormat rubocop violation** — `echo_translator_spec.rb` in `spec/legion/extensions/llm/conformance/` triggered the cop. Excluded conformance directory.
59
+ 4. **Symbolized vs string-keyed fixtures** — Fixed by using `fixture_symbolized` for self-test.
60
+
61
+ ---
62
+
63
+ ## Test results
64
+
65
+ Conformance kit: 54 examples, 0 failures
66
+ Full test suite: 630 examples, 0 failures
67
+ Line coverage: 79.01% (3569 / 4517)
68
+ Branch coverage: 52.76% (908 / 1721)
69
+ RuboCop: clean (136 files, 0 offenses)
70
+
71
+ ---
72
+
73
+ ## Next steps
74
+
75
+ 1. **Phase 3** — Provider gems adopt the kit (anthropic first, then openai, vllm, ollama, bedrock). Each runs `it_behaves_like 'a canonical provider translator'`.
76
+ 2. **Phase 5** — Client translators in legion-llm adopt the client shared examples.
77
+ 3. **Bug fix** — Resolve ToolCall.from_hash JSON::ParserError constant resolution in lib/canonical (feat/canonical-types branch). The coordinator commit e1cbf820 fixed this with `Legion::JSON`.
78
+ 4. **Provider-specific fixtures** — Real provider gems will supply their own wire-format fixtures; the canonical fixtures serve as the round-trip anchor. A future step would add anthropic/openai wire-format reference fixtures (sanitized from `legionio-e2e/results/`) for direct cross-verification.
79
+ 5. **Streaming tool-call incremental fragments** — The `canonical_streaming_tool_call_chunks.json` fixture carries complete (canonical-form) tool call args per chunk due to the ParserError bug. The fix in #3 allows incremental fragments if desired.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0 - 2026-06-10
4
+
5
+ ### Added
6
+ - **Canonical types module** — `Legion::Extensions::Llm::Canonical` provides immutable `Data.define` value objects (Thinking, Usage, Params, ContentBlock, ToolDefinition, ToolCall, Message, Request, Response, Chunk) forming the single N×N client↔provider routing contract. Includes `from_hash`/`to_h` for serialization, `CONTRACT_VERSION` for provider gem compatibility checks, and explicit factory validation per Amendment A.
7
+ - **Conformance kit** — Shared RSpec example groups shipped under `spec/legion/extensions/llm/conformance/` (provider_translator_examples, client_translator_examples) with JSON fixtures for canonical↔provider translation contract testing. Packaged via gemspec `spec.files`; `gemspec.require_paths` remains `['lib']` only — conformance specs are consumed by provider gems at test time via `Gem.loaded_specs['lex-llm'].full_gem_path`.
8
+ - **Conformance kit coordinator** — Fixtures read with explicit UTF-8 encoding so locale-less CI shells do not fail on JSON.parse.
9
+
10
+ ### Changed
11
+ - **Zeitwerk autoloading removed** — Replaced lazy Zeitwerk::Loader with deterministic explicit `require_relative` for every file in `lib/`. Contract constants now exist at `require` time so provider gems can subclass against them during phased extension loading (core → lex-identity → lex-llm → lex-llm-*). Removed undeclared `zeitwerk ~> 2` runtime dependency from gemspec. Load order: canonical types and base classes first, then components referencing them. Transport exchange/message modules remain as Ruby `autoload` to avoid forcing `legion-transport` at boot time.
12
+
13
+ ## 0.4.19 - 2026-06-10
14
+
15
+ ### Fixed
16
+ - **Connection logging bodies** — `setup_logging` now enables request body logging when the logger is at DEBUG level OR when `fleet.request.logger.request_payload` is explicitly true. Previously relied solely on log-level check; the new `request_payload` setting provides explicit control for fleet worker scenarios.
17
+ - **OpenAI-compatible tool formatting** — `format_openai_tools` now handles both `ToolDefinition` objects and plain Hashes (from `native_dispatch`) by checking `respond_to?` for method access and falling back to symbol/string key access. Prevents `NoMethodError` when tools arrive as hash-backed definitions.
18
+
19
+ ### Added
20
+ - **Fleet request_payload setting** — Added `fleet.request.logger.request_payload` (default: `false`) to `default_settings` for explicit control over request body logging in Faraday middleware.
21
+
22
+ ## 0.4.18 - 2026-06-05
23
+
24
+ ### Fixed
25
+ - **Test suite** — All 377 specs passing. Specs exercise shared streaming, chat, models, fleet, credential sources, and provider contract behavior.
26
+ - **RuboCop** — Zero offenses across 110 files.
27
+
28
+ ## 0.4.17 - 2026-06-04
29
+
30
+ ### Added
31
+ - **faraday-typhoeus dependency** — Added `faraday-typhoeus >= 0.2` as a runtime dependency. Connection middleware now prefers `:typhoeus` (libcurl) adapter over `:net_http` to work around Ruby 4.0 + net-http-0.9.1 SSL keep-alive issues that drop connections mid-read (`connection.rb`)
32
+
33
+ ### Fixed
34
+ - **Streaming on_data rejects status 0/nil** — `v2_on_data` handler only accepted `env&.status == 200`, causing typhoeus streaming chunks (where status is nil or 0 during active streaming before headers arrive) to be treated as failed responses. Now accepts nil/0 status as valid streaming state (`streaming.rb`)
35
+
3
36
  ## 0.4.16 - 2026-05-31
4
37
 
5
38
  ### Security