docs(spring-ai): reflect cycles-spring-ai-starter 0.3.0#635
Merged
Conversation
v0.3.0 of cycles-spring-ai-starter shipped to Maven Central on
2026-05-12 (artifact verified live at repo1.maven.org). The release
adds three new extension points (pluggable SubjectResolver, pluggable
PromptTokenEstimator with optional jtokkit BPE impl, cycles.reservation_id
on chat-client traces) and an end-to-end integration test, on top of
the v0.2.0 feature surface. No breaking changes vs v0.2.0.
Changes
- changelog.md ecosystem table: cycles-spring-ai-starter row bumped
0.2.0 -> 0.3.0; description rewritten to lead with the three new
extension points and the 100/100 coverage marker.
- how-to/integrating-cycles-with-spring-ai.md:
- Maven and Gradle dep snippets bumped 0.2.0 -> 0.3.0.
- "What v0.2.0 covers" section renamed to "What v0.3.0 covers".
Existing 0.2.0 bullets kept (the features are still here, that's
the point of a non-breaking minor); ObservationConvention bullet
extended to note the new high-cardinality cycles.reservation_id
KeyValue and the emit-reservation-id-on-trace opt-out.
- New subsection "New extension points in v0.3.0" with:
* SubjectResolver bean example for per-request attribution
(tenant from SecurityContextHolder)
* Property+dependency snippet for opting into jtokkit-based
BPE token estimation
* Pointer to the README's Extension Points section for full
details
- README link target #whats-new-in-020 -> #whats-new-in-030.
- SEO description updated to mention v0.3.0 features (jtokkit,
pluggable subject routing).
- application.yml example: dropped the stale "v0.2 will derive from
prompt size" inline comment in favor of "set estimate-from-prompt=
true to derive from prompt size" since both ends of that path are
shipped.
Verification
- Maven Central confirmed live: maven-metadata.xml shows 0.3.0 as
latest/release; all five 0.3.0 artifacts (pom, jar, sources,
javadoc, GPG signature) respond 200 at repo1.maven.org.
- No stale 0.2.0 spring-ai-starter references left:
grep -n "spring-ai-starter.{0,30}0\.2\.0" -> no matches
grep -n "whats-new-in-020" -> no matches
Found by a review pass after the v0.3.0 framing changes. Eight stale or inaccurate claims spread across two files; all in the docs-fix-related-to-this-PR area, so bundling rather than opening a separate cleanup PR. how-to/integrating-cycles-with-spring-ai.md - Comparison table (line ~19): "Spring AI CallAdvisor + ChatClientCustomizer (auto-wired)" was incomplete — streaming advisor + tool gate have been auto-configured since 0.2.0. Updated to: "Spring AI CallAdvisor + StreamAdvisor + ChatClientCustomizer (auto-wired); CyclesToolGate for per-tool gating". - Comparison table "Where it intercepts" row: only mentioned .call() — also intercepts .stream() (auto) and tools (when wrapped). Updated. - Comparison table "Call-site changes": clarified that tool wrapping is opt-in even though chat advisor attachment is transparent. - Comparison table "Estimate computation" row: said "Fixed constant (v0.1.0); per-call derivation in v0.2". v0.2.0 shipped chars/4 derivation; v0.3.0 made the estimator pluggable with a jtokkit BPE option. Rewritten to describe the current state. - Comparison table: added a "Subject routing" row to mirror the pluggable SubjectResolver added in 0.3.0 (the boot starter side uses SpEL; the AI starter side uses a bean). - Path-1 intro: "set 6 properties" was accurate at v0.1.0; with v0.3.0 the configuration surface is 13 properties. Rephrased to "configure a few cycles.* properties" without pinning a count (less likely to drift). - "Don't double-charge" warning: README anchor #%EF%B8%8F-the-double-charge-gotcha didn't match the actual GitHub-rendered anchor (GitHub strips emoji + variation selectors from header anchors). Fixed to #the-double-charge-gotcha — the link now resolves. - Path-2 "Prerequisites" dep snippet: Maven version pinned cycles-client-java-spring at 0.3.0 (doesn't exist; latest released is 0.2.2) and Gradle pinned 0.2.0 (also wrong). Both corrected to 0.2.2 — what's actually live on Maven Central. These were stale before my v0.3.0 PR but surfaced in this review. how-to/ecosystem.md - "Spring AI Starter (advisor-based)" description: said the starter "auto-wires a CallAdvisor onto every ChatClient" — accurate at 0.1.0, incomplete now. Updated to mention CallAdvisor + StreamAdvisor (both auto-wired), CyclesToolGate (opt-in per-tool), pluggable SubjectResolver, and the ObservationConvention's cycles.reservation_id emission. No corresponding source-code claims; these are pure docs accuracy fixes against the actual v0.3.0 surface that already shipped to Maven Central.
…se case"
Review of quickstart/how-to-add-hard-budget-limits-to-spring-ai-with-cycles.md
against the v0.3.0 starter surface. The doc is conceptual (no Maven
coordinates, no property names, no code examples), so most v0.3.0
accuracy concerns don't apply. Three issues fixed:
1. Wrong starter link in Next steps
The "Next steps" section had a bullet:
Integrate with Spring AI using the [Spring Client](
https://github.com/runcycles/cycles-spring-boot-starter)
That's the wrong starter. cycles-spring-boot-starter is for generic
Spring Boot @cycles annotation use (non-Spring-AI code paths).
For Spring AI specifically, the canonical integration is
cycles-spring-ai-starter — which is exactly what this entire
guide is about. A reader following the guide's call-to-action would
end up at the wrong repo.
Rewrote the Next steps to:
- Lead with a "Start here" pointer to the existing
/how-to/integrating-cycles-with-spring-ai guide (which has the
concrete Maven setup, config reference, and v0.3.0 extension-point
examples).
- Link cycles-spring-ai-starter as the actual starter, with a
one-line note that cycles-spring-boot-starter is the companion
for non-Spring-AI code paths (so readers who DO need that path
still find it).
- Demoted the broader Cycles stack links into a separate "broader
stack" group below — they were drowning the AI-specific guidance.
2. "Common first use case" was abstract about what "minimal
architecture change" actually means
The doc said "guard every Spring AI model call with a Cycles
reservation ... gives you immediate value with minimal architecture
change". True but vague — a reader doesn't know if "minimal" means
"write a custom interceptor" or "annotate every method" or "add a
dep". Made it concrete: literally a Maven dep + a few cycles.spring-ai.*
properties; the auto-wired CallAdvisor + StreamAdvisor handle every
ChatClient call and stream without call-site changes.
3. Same section: per-tenant attribution was listed as "set the tenant
in properties" implicitly
Mentioned that v0.3.0's SubjectResolver lets you pull tenant from an
authenticated principal at request time, not just from the
static-property defaults. Important for multi-tenant SaaS — the most
common case for hard-budget enforcement.
Also clarified that tool gating is opt-in (cyclesToolGate.wrap), not
auto-applied like chat — matches the actual v0.3.0 behavior and
avoids over-promising.
Two more issues from a deeper review pass on
how-to/integrating-cycles-with-spring-ai.md. (A third — significant
microcents math discrepancy — is flagged in the review summary but
intentionally not fixed in this commit; it needs to be addressed in
lockstep across the docs + the cycles-spring-ai-starter properties
javadoc and README, which is multi-repo coordination.)
1. Duplicate top-level `cycles:` key in the application.yml example
The Path 1 Configure example had two `cycles:` blocks at the top
level:
cycles:
base-url: ...
tenant: acme
app: my-spring-ai-app
cycles:
spring-ai:
enabled: true
...
YAML's spec does not allow duplicate top-level keys. SnakeYAML
(Spring Boot's parser) tolerates this by merging or last-wins-ing,
but: (a) some other YAML tooling errors; (b) it's confusing to
readers who copy the snippet; (c) it suggests a structure
("cycles" appears twice") that isn't real. Merged into a single
top-level cycles: with spring-ai as a child node — the canonical
shape that any YAML parser handles correctly.
2. Next steps section pointed only at Path 2 resources
The "Next steps" at the end of the file linked exclusively to
cycles-spring-boot-starter resources (Quickstart, SpEL reference,
Spring Client configuration). A reader who followed Path 1 in this
guide would scroll to Next steps expecting AI-starter-specific
follow-ups and find none of them — the only AI-starter link was
the earlier "See README..." line in the body.
Split the Next steps into two groups:
- Path 1 resources: cycles-spring-ai-starter README anchor (which
has Extension Points, Quick Start, double-charge gotcha),
Maven Central artifact link, and the existing strategic
Budget Limits with Spring AI quickstart.
- Path 2 resources: kept the existing four bullets verbatim.
This gives both readerships a place to go after finishing the
guide.
What's NOT in this commit (flagged for separate decision)
The cost-per-token examples used throughout the file (and the
cycles-spring-ai-starter properties javadoc / README) say:
GPT-4o input $2.50 / 1M tokens = 25 USD_MICROCENTS per token
GPT-4o output $10.00 / 1M tokens = 100 USD_MICROCENTS per token
The Unit.java canonical definition says:
1 USD = 100,000,000 microcents (i.e. 1 microcent = 1e-8 USD).
By that definition:
$2.50 / 1M = 250 microcents per token (not 25)
$10.00 / 1M = 1000 microcents per token (not 100)
So the examples are off by 10x — users who copy them set
estimates / actuals that under-bill by an order of magnitude.
The bug is in BOTH the docs site and the
cycles-spring-ai-starter source (CyclesSpringAiProperties.java
javadoc + README config-reference table). Needs coordinated fix
across both repos; not silently changing it in the docs alone
because that would create an inconsistency that's worse than the
status quo.
Companion to cycles-spring-ai-starter PR #30. The cost-per-token examples in how-to/integrating-cycles-with-spring-ai.md and the cycles-spring-ai-starter's own javadoc / README all said: GPT-4o input $2.50/1M tokens = 25 microcents/token GPT-4o output $10.00/1M tokens = 100 microcents/token Unit.java in the parent SDK is the canonical definition: "US dollars in microcents (1 USD = 100,000,000 microcents)" Working through the conversion: $2.50 / 1M tokens × 100M microcents/USD = 250 microcents/token $10.00 / 1M tokens × 100M microcents/USD = 1000 microcents/token So the examples are off by 10x. Users copying the snippets set budgets that under-count actual provider cost by an order of magnitude — budgets blow through silently because the accounting under-reports. Fifteen occurrences in this file, fixed atomically: - Comparison table: `@Cycles("#tokens * 250")` (was * 25) - jtokkit YAML opt-in block: input-cost-per-token: 250, output-cost- per-token: 1000 (was 25 / 100); added an inline comment on the input line spelling out the conversion (1 USD = 100M MICROCENTS) so future readers don't have to re-derive it. - Ten SpEL @cycles(value = "#maxTokens * 250", ...) examples in Path 2 (was * 25) - One @cycles(value = "#prompt.length() / 4 * 250", ...) example - Two Java `maxTokens * 250L` / `tokenCount.get() * 250L` multiplications in the streaming-with-CyclesClient example - Two comment lines on the gpt-4o examples updated to reflect the corrected microcent value The starter-side PR (runcycles/cycles-spring-ai-starter#30) fixes the property javadoc + README config-reference table in lockstep. Both should land before any release notes or external content references the existing wrong numbers. Per-call dollar amounts in the file (e.g. `@Cycles("500000", // $0.005 per tool call ...)`) are NOT changed — they're correct ($0.005 = 500,000 microcents). Only the per-token factors are off.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
v0.3.0 of
cycles-spring-ai-startershipped to Maven Central earlier today (artifact verified live atrepo1.maven.org/maven2/io/runcycles/cycles-spring-ai-starter/0.3.0/). The release adds three new extension points + an end-to-end integration test on top of v0.2.0. No breaking changes.Files changed
changelog.mdecosystem table —cycles-spring-ai-starterrow:0.2.0→0.3.0, description rewritten to lead with the three new extension points and the 100/100 coverage marker.how-to/integrating-cycles-with-spring-ai.md:0.2.0→0.3.0.cycles.reservation_idhigh-cardinality KeyValue.SubjectResolver(tenant fromSecurityContextHolder) and the jtokkit BPE opt-in (property + Maven dep snippet).#whats-new-in-020→#whats-new-in-030.descriptionfrontmatter updated.application.ymlexample — both ends of that path have shipped.Verification
maven-metadata.xmlshows0.3.0as<latest>and<release>; all five 0.3.0 artifacts (pom, jar, sources, javadoc, GPG signature) respond 200.0.2.0spring-ai-starter references remain (grepped).Test plan