From 968602d59d355d702d3dd6d7fa20232fe543d71d Mon Sep 17 00:00:00 2001 From: Nikhil Pareek Date: Wed, 30 Sep 2026 14:13:26 -0700 Subject: [PATCH] docs(spring-ai): replace obsolete setup with compatibility warning --- scripts/spring-ai-warning.test.mjs | 48 +++ .../docs/integrations/traceai/spring-boot.mdx | 342 +----------------- 2 files changed, 65 insertions(+), 325 deletions(-) create mode 100644 scripts/spring-ai-warning.test.mjs diff --git a/scripts/spring-ai-warning.test.mjs b/scripts/spring-ai-warning.test.mjs new file mode 100644 index 000000000..4f7d3e8d7 --- /dev/null +++ b/scripts/spring-ai-warning.test.mjs @@ -0,0 +1,48 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; + +// Source contract only; the site build validates MDX rendering separately. +// Run with: node --test scripts/spring-ai-warning.test.mjs +const source = readFileSync( + new URL("../src/pages/docs/integrations/traceai/spring-boot.mdx", import.meta.url), + "utf8", +); +const warning = source.match(/([\s\S]*?)<\/Warning>/)?.[1] ?? ""; + +test("warning identifies the unsupported Spring AI 1.1 path and reported linkage error", () => { + assert.match(warning, /not a supported integration path for Spring AI `1\.1\.x`/); + assert.match(warning, /`NoSuchMethodError` involving `Message\.getContent\(\)` has been reported/); + assert.match(warning, /does not yet provide a verified replacement artifact coordinate/); +}); + +test("M4 is a historical wrapper compilation target, not a tested support range", () => { + assert.match(warning, /M4-based traceAI wrapper was compiled against Spring AI `1\.0\.0-M4`/); + assert.match(warning, /compilation target, not a tested support range/); + assert.ok(!/source currently targets/i.test(source), "Do not describe the current source target"); +}); + +test("page has no open-ended M4 compatibility range", () => { + assert.ok(!/M4\s*\+/.test(source), "Remove the M4+ support claim"); +}); + +test("page has no floating main-SNAPSHOT coordinate", () => { + assert.ok(!/main-SNAPSHOT/i.test(source), "Remove the floating main-SNAPSHOT coordinate"); +}); + +for (const [name, url] of [ + ["Spring AI 1.1 observability", "https://docs.spring.io/spring-ai/reference/1.1/observability/index.html"], + ["Spring Boot 3.5 tracing", "https://docs.spring.io/spring-boot/3.5/reference/actuator/tracing.html"], +]) { + test(`page links to the versioned official ${name} reference`, () => { + assert.ok(source.includes(`](${url})`), `Missing versioned reference: ${url}`); + }); +} + +test("warning replaces obsolete installation examples and provider tables", () => { + assert.ok(!/^\s*(?:```|~~~|\|)/m.test(source), "Remove code fences and tables from this notice"); + assert.ok( + !/com\.github\.future-agi\.traceAI|traceai-spring-boot-starter|TracedChatModel|TracedEmbeddingModel/.test(source), + "Remove obsolete dependency coordinates and wrapper examples", + ); +}); diff --git a/src/pages/docs/integrations/traceai/spring-boot.mdx b/src/pages/docs/integrations/traceai/spring-boot.mdx index f6c6efb13..d1a5ae335 100644 --- a/src/pages/docs/integrations/traceai/spring-boot.mdx +++ b/src/pages/docs/integrations/traceai/spring-boot.mdx @@ -1,339 +1,31 @@ --- -title: "Spring Boot and Spring AI Integration with Future AGI" -description: "Add tracing to Spring Boot apps with Spring AI. Configure application.yml, wrap your ChatModel and EmbeddingModel, and traces are collected automatically." +title: "Choose a tracing path for Spring AI" +description: "Check the traceAI wrapper's Spring AI compatibility before installing it, and use native Spring AI observability while a verified setup is unavailable." --- - -- `traceai-spring-boot-starter` auto-configures `FITracer` from `application.yml` -- Wrap `ChatModel` with `TracedChatModel`, `EmbeddingModel` with `TracedEmbeddingModel` -- Captures messages, token counts, model info, latency, and errors -- Streaming support built in - works with `Flux` -- Distributed via JitPack (no Maven Central publish yet) - +## About -## How it works +Before you add traceAI wrappers to a Spring AI application, check the compatibility limitation below. -`traceai-spring-boot-starter` is the Spring Boot auto-configuration for TraceAI. When you add it to your project: + +The M4-based traceAI wrapper was compiled against Spring AI `1.0.0-M4`. This is a compilation target, not a tested support range. The M4-based wrapper is not a supported integration path for Spring AI `1.1.x`: a `NoSuchMethodError` involving `Message.getContent()` has been reported. -1. `TraceAIAutoConfiguration` reads your `traceai.*` properties and creates an `FITracer` bean -2. You wrap your Spring AI models with `TracedChatModel` or `TracedEmbeddingModel` -3. Every call and stream through those wrappers creates an OpenTelemetry span with LLM metadata attached +Do not use the historical M4 installation examples as a Spring AI 1.1 setup. This page does not yet provide a verified replacement artifact coordinate. + -The wrappers delegate to the underlying model and add span instrumentation around each call. You pick which models get traced by wrapping them explicitly - the starter doesn't auto-wrap beans because that could break apps with multiple providers or custom bean ordering. +## Use native Spring observability meanwhile -## 1. Add dependencies +For a Spring AI 1.1 application, follow the official [Spring AI 1.1 observability guide](https://docs.spring.io/spring-ai/reference/1.1/observability/index.html). For a Spring Boot 3.5 application, use the matching [Spring Boot tracing guide](https://docs.spring.io/spring-boot/3.5/reference/actuator/tracing.html) for OpenTelemetry and OTLP configuration. -Add the JitPack repository and the starter to your `pom.xml`. This assumes you're using the Spring Boot parent POM: +Keep your working native observability configuration rather than adding the M4-based traceAI wrapper. Native observations are an interim tracing path, not a guarantee of identical traceAI attributes, token counts or cost fields across providers. Do not enable sensitive prompt or completion logging just to work around this compatibility issue. -```xml - - org.springframework.boot - spring-boot-starter-parent - 3.2.1 - +## Check compatibility before installing - - 17 - 1.0.0-M4 - +The previous M4 dependency blocks, provider table and wrapper examples have been removed from this page until a replacement installation can be verified. This is not a recommendation to downgrade Spring AI. - - - - spring-milestones - https://repo.spring.io/milestone - +A source version pin does not establish runtime compatibility. Use only an artifact coordinate and exact Java, Spring AI and Spring Boot combination that have been verified together. No compatibility claim for Spring AI 1.0 GA, Spring AI 2.0 or later releases follows from the historical M4 compilation target. - - - jitpack.io - https://jitpack.io - - +## Next Steps - - - - org.springframework.boot - spring-boot-starter-web - - - - - com.github.future-agi.traceAI - traceai-spring-boot-starter - main-SNAPSHOT - - - - - org.springframework.ai - spring-ai-openai-spring-boot-starter - ${spring-ai.version} - - -``` - -For Gradle: - -```groovy -ext { - springAiVersion = '1.0.0-M4' -} - -repositories { - maven { url 'https://repo.spring.io/milestone' } - maven { url 'https://jitpack.io' } -} - -dependencies { - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'com.github.future-agi.traceAI:traceai-spring-boot-starter:main-SNAPSHOT' - implementation "org.springframework.ai:spring-ai-openai-spring-boot-starter:${springAiVersion}" -} -``` - -**Requirements:** Java 17+, Spring Boot 3.2+, Spring AI 1.0.0-M4+ - ---- - -## 2. Configure application.yml - -```yaml -spring: - application: - name: my-spring-ai-app - ai: - openai: - api-key: ${OPENAI_API_KEY} - chat: - options: - model: gpt-4o-mini - temperature: 0.7 - -traceai: - enabled: true - base-url: https://api.futureagi.com - api-key: ${FI_API_KEY} - secret-key: ${FI_SECRET_KEY} - project-name: my-spring-ai-app -``` - -### All configuration properties - -| Property | Type | Default | What it does | -|----------|------|---------|-------------| -| `traceai.enabled` | boolean | `true` | Disables all TraceAI instrumentation when set to `false` | -| `traceai.base-url` | string | - | FutureAGI API endpoint | -| `traceai.api-key` | string | - | Your FI_API_KEY | -| `traceai.secret-key` | string | - | Your FI_SECRET_KEY | -| `traceai.project-name` | string | - | Project name in FutureAGI dashboard | -| `traceai.service-name` | string | `spring.application.name` | Service name in traces (falls back to app name) | -| `traceai.hide-inputs` | boolean | `false` | Redact all input values from spans | -| `traceai.hide-outputs` | boolean | `false` | Redact all output values from spans | -| `traceai.hide-input-messages` | boolean | `false` | Redact input messages specifically | -| `traceai.hide-output-messages` | boolean | `false` | Redact output messages specifically | -| `traceai.enable-console-exporter` | boolean | `false` | Print spans to console (useful for debugging) | -| `traceai.batch-size` | int | `512` | Spans per export batch | -| `traceai.export-interval-ms` | long | `5000` | How often to flush spans (ms) | - ---- - -## 3. Wrap your models - -The starter auto-creates the `FITracer` bean. You just need to wrap your Spring AI models. - -### Chat model - -```java -import ai.traceai.FITracer; -import ai.traceai.spring.TracedChatModel; -import org.springframework.ai.chat.model.ChatModel; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class TraceAIConfig { - - @Bean - public TracedChatModel tracedChatModel(ChatModel chatModel, FITracer tracer) { - // "openai" = provider name, used in span attributes - return new TracedChatModel(chatModel, tracer, "openai"); - } -} -``` - -`TracedChatModel` implements `ChatModel`, so you can inject it anywhere you'd use a regular `ChatModel`. - -### Embedding model - -Add this to the same `@Configuration` class: - -```java -import ai.traceai.spring.TracedEmbeddingModel; -import org.springframework.ai.embedding.EmbeddingModel; - -@Bean -public TracedEmbeddingModel tracedEmbeddingModel(EmbeddingModel embeddingModel, FITracer tracer) { - return new TracedEmbeddingModel(embeddingModel, tracer, "openai"); -} -``` - -### Using the global tracer - -Both wrappers have a two-arg constructor that uses the global tracer instead of injecting `FITracer`. This only works after the auto-configuration has run (i.e., inside Spring-managed beans, not in static initializers or tests): - -```java -// Uses TraceAI.getTracer() internally - requires TraceAI.init() to have been called -TracedChatModel traced = new TracedChatModel(chatModel, "openai"); -TracedEmbeddingModel tracedEmbed = new TracedEmbeddingModel(embeddingModel, "openai"); -``` - ---- - -## 4. Use it - -Once wrapped, use your models normally. Tracing is automatic. - -### Basic chat - -```java -import ai.traceai.spring.TracedChatModel; -import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/chat") -public class ChatController { - - private final TracedChatModel chatModel; - - @Autowired - public ChatController(TracedChatModel chatModel) { - this.chatModel = chatModel; - } - - @GetMapping - public String chat(@RequestParam String message) { - var response = chatModel.call(new Prompt(message)); - return response.getResult().getOutput().getContent(); - } - - @PostMapping - public String chatPost(@RequestBody ChatRequest request) { - var response = chatModel.call(new Prompt(request.message())); - return response.getResult().getOutput().getContent(); - } - - record ChatRequest(String message) {} -} -``` - -### Streaming - -Streaming requires `spring-boot-starter-webflux` on the classpath alongside `spring-boot-starter-web`. - -```java -import org.springframework.ai.chat.prompt.Prompt; -import reactor.core.publisher.Flux; - -@GetMapping(value = "/stream", produces = "text/event-stream") -public Flux stream(@RequestParam String message) { - return chatModel.stream(new Prompt(message)) - .map(response -> response.getResult().getOutput().getContent()); -} -``` - -The streaming wrapper accumulates chunks and records the full output in the span when the stream completes. - ---- - -## What gets captured - -Every `TracedChatModel.call()` creates a span with: - -| Attribute | Example value | -|-----------|--------------| -| `llm.system` | `spring-ai` | -| `llm.provider` | `openai` | -| `llm.request.model` | `gpt-4o-mini` | -| `llm.response.model` | `gpt-4o-mini-2024-07-18` | -| `llm.request.temperature` | `0.7` | -| `llm.request.top_p` | `1.0` | -| `llm.token_count.prompt` | `15` | -| `llm.token_count.completion` | `42` | -| `llm.token_count.total` | `57` | -| `input.value` | Full prompt text | -| `output.value` | Full response text | -| Input/output messages | Structured role + content pairs | - -`TracedEmbeddingModel.call()` spans capture the same `llm.system`, `llm.provider`, and model attributes, plus embedding-specific ones: `embedding.vector_count`, `embedding.dimensions`, `embedding.model_name`, and token counts (`llm.token_count.prompt`, `llm.token_count.total`). - -Errors on both wrappers are captured with full stack traces and set the span status to `ERROR`. - ---- - -## Disabling tracing - -Set `traceai.enabled: false` in your `application.yml`. The auto-configuration won't create any beans, and your app runs without any TraceAI overhead. - -For per-environment control: - -```yaml -# application-prod.yml -traceai: - enabled: true - hide-inputs: true - hide-outputs: true - -# application-dev.yml -traceai: - enabled: true - enable-console-exporter: true - -# application-test.yml -traceai: - enabled: false -``` - ---- - -## Debugging - -Enable console export and DEBUG logging to see spans printed to stdout: - -```yaml -traceai: - enable-console-exporter: true - -logging: - level: - ai.traceai: DEBUG -``` - -Check that `TraceAI` initialized: - -```java -if (ai.traceai.TraceAI.isInitialized()) { - System.out.println("TraceAI version: " + ai.traceai.TraceAI.getVersion()); -} -``` - ---- - -## Supported providers - -The `provider` string you pass to `TracedChatModel` / `TracedEmbeddingModel` is just a label in span attributes. You can use any Spring AI provider: - -| Spring AI starter | Provider string | -|-------------------|----------------| -| `spring-ai-openai-spring-boot-starter` | `"openai"` | -| `spring-ai-anthropic-spring-boot-starter` | `"anthropic"` | -| `spring-ai-azure-openai-spring-boot-starter` | `"azure-openai"` | -| `spring-ai-vertex-ai-gemini-spring-boot-starter` | `"vertex-ai"` | -| `spring-ai-bedrock-ai-spring-boot-starter` | `"bedrock"` | -| `spring-ai-ollama-spring-boot-starter` | `"ollama"` | -| `spring-ai-mistral-ai-spring-boot-starter` | `"mistral"` | - -Just swap the Spring AI dependency and change the provider string. The tracing wrapper doesn't care which provider is underneath. +- Use the [Spring AI 1.1 observability reference](https://docs.spring.io/spring-ai/reference/1.1/observability/index.html) to review model and client observations. +- Use the [Spring Boot 3.5 tracing reference](https://docs.spring.io/spring-boot/3.5/reference/actuator/tracing.html) to configure your tracing exporter.