Skip to content

CAMEL-24559: Extend GenAI observability to OpenAI embeddings, moderation, and responses - #26106

Open
atiaomar1978-hub wants to merge 2 commits into
apache:mainfrom
atiaomar1978-hub:cursor/CAMEL-24559-openai-genai-observability-c587
Open

CAMEL-24559: Extend GenAI observability to OpenAI embeddings, moderation, and responses#26106
atiaomar1978-hub wants to merge 2 commits into
apache:mainfrom
atiaomar1978-hub:cursor/CAMEL-24559-openai-genai-observability-c587

Conversation

@atiaomar1978-hub

Copy link
Copy Markdown
Contributor

Summary

Follow-up to CAMEL-23861 / CAMEL-24559: extend GenAI observability to additional OpenAI producer operations beyond chat-completion.

This PR instruments OpenAI embeddings, moderation, and responses with the existing GenAiObservability API.

Operation gen_ai.operation.name
openai:embeddings embeddings
openai:moderation moderation (Camel extension)
openai:responses chat
openai:chat-completion chat (unchanged)

Changes

  • Instrument OpenAIEmbeddingsProducer, OpenAIModerationProducer, OpenAIResponsesProducer
  • Add GenAiOperationName.MODERATION
  • Tests: OpenAIEmbeddingsObservabilityTest, OpenAIModerationObservabilityTest, OpenAIResponsesObservabilityTest
  • Docs: ai-observability.adoc, catalog mirror, 4.23 upgrade guide

Test plan

./mvnw -pl components/camel-ai/camel-openai -am test -Dtest=OpenAIEmbeddingsObservabilityTest,OpenAIModerationObservabilityTest,OpenAIResponsesObservabilityTest

Images, audio, Spring AI modules, and cloud LLMs remain for future CAMEL-24559 follow-ups.


AI-generated PR description on behalf of @atiaomar1978-hub

cursoragent and others added 2 commits September 4, 2026 03:11
…n, and responses

Extend camel-openai producers beyond chat-completion with OpenTelemetry
spans and Micrometer metrics using the existing GenAiObservability API.

- Instrument OpenAIEmbeddingsProducer, OpenAIModerationProducer, and
  OpenAIResponsesProducer with try/recordSuccess/recordError/close
- Add GenAiOperationName.MODERATION for content-policy operations
- Add OpenAIEmbeddingsObservabilityTest, OpenAIModerationObservabilityTest,
  OpenAIResponsesObservabilityTest, and shared test support
- Document OpenAI operation coverage in ai-observability.adoc and 4.23
  upgrade guide

Co-authored-by: Cursor Agent <noreply@cursor.com>
- Scope GenAI spans to SDK calls only (embeddings/moderation)
- Map openai:responses to gen_ai.operation.name=chat per OTel OpenAI usage
- Clarify MODERATION as Camel extension in enum javadoc and docs
- Strengthen tests: body/header assertions, exact token tags, override properties

Co-authored-by: Cursor Agent <noreply@cursor.com>
@atiaomar1978-hub

Copy link
Copy Markdown
Contributor Author

Links CAMEL-24559 — first follow-up slice for extending GenAI observability beyond chat/streaming.

Review status: Bugbot (no bugs) and Grok review feedback addressed in commit 38b10e2fc6c (SDK-only span scope, chat for responses, docs, stronger tests).


AI-generated comment on behalf of @atiaomar1978-hub

@atiaomar1978-hub

Copy link
Copy Markdown
Contributor Author

Links CAMEL-24559 — first follow-up slice. Bugbot + Grok review feedback addressed in 38b10e2.

@atiaomar1978-hub
atiaomar1978-hub marked this pull request as ready for review September 4, 2026 03:40
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • catalog/camel-catalog
  • components/camel-ai/camel-ai-observability-api
  • components/camel-ai/camel-ai-observability
  • components/camel-ai/camel-openai
  • docs

ℹ️ Dependent modules were not tested because the total number of affected modules exceeded the threshold (50). Use the test-dependents label to force testing all dependents.


🔬 Scalpel shadow comparison — Scalpel: 73 tested, 25 compile-only — current: 71 all tested

Maveniverse Scalpel detected 98 affected modules (current approach: 71).

⚠️ Modules only in Scalpel (27)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

Skip-tests mode would test 73 modules (5 direct + 68 downstream), skip tests for 25 (generated code, meta-modules)

Modules Scalpel would test (73)
  • camel-a2a
  • camel-ai-observability
  • camel-ai-observability-api
  • camel-as2
  • camel-catalog
  • camel-clickup
  • camel-consul
  • camel-core-all
  • camel-csv
  • camel-fhir
  • camel-groovy
  • camel-hazelcast
  • camel-hl7
  • camel-ibm-watsonx-ai
  • camel-iso8583
  • camel-java-joor-dsl
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-jetty
  • camel-jsonpath
  • camel-kamelet
  • camel-kamelet-main-support
  • camel-knative-http
  • camel-langchain4j-agent
  • camel-langchain4j-chat
  • camel-langchain4j-embeddings
  • camel-langchain4j-embeddingstore
  • camel-launcher-container
  • camel-mail
  • camel-mail-microsoft-oauth
  • camel-main
  • camel-maven-plugin
  • camel-mcp-server
  • camel-micrometer-prometheus
  • camel-microprofile-health
  • camel-netty
  • camel-netty-http
  • camel-oauth
  • camel-observability-services
  • camel-once
  • camel-openai
  • camel-openapi-validator
  • camel-platform-http-main
  • camel-platform-http-vertx
  • camel-plc4x
  • camel-pubnub
  • camel-quickfix
  • camel-rest-openapi
  • camel-restdsl-openapi-plugin
  • camel-salesforce
  • camel-salesforce-codegen
  • camel-salesforce-maven-plugin
  • camel-spring-ai-chat
  • camel-spring-main
  • camel-state-store
  • camel-syslog
  • camel-telegram
  • camel-test-main-junit5
  • camel-test-main-junit6
  • camel-vertx-websocket
  • camel-webhook
  • camel-whatsapp
  • camel-xml-io-dsl
  • camel-xml-jaxb-dsl
  • camel-xml-jaxb-dsl-test-definition
  • camel-xml-jaxb-dsl-test-spring
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
  • camel-zookeeper
  • docs
Modules with tests skipped (25)
  • apache-camel
  • camel-allcomponents
  • camel-catalog-console
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

⚠️ Some tests are disabled on GitHub Actions (@DisabledIfSystemProperty(named = "ci.env.name")) and require manual verification:

  • components/camel-ai/camel-openai: 7 test(s) disabled on GitHub Actions
Build reactor — dependencies compiled but only changed modules were tested (5 modules)
  • Camel :: AI :: Observability
  • Camel :: AI :: Observability API
  • Camel :: AI :: OpenAI
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Docs

⚙️ View full build and test results

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary: Solid and well-structured extension of GenAI observability to three additional OpenAI producers. The pattern follows the established createChatCompletion in OpenAIProducer closely. Two issues worth addressing before merge.

This review was generated by an automated reviewer (Hermès) on behalf of @gnodet.

calculateSimilarityIfRequested(exchange, embeddings);
}

private static Integer toTokenCount(long tokens) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Math.toIntExact(tokens) will throw ArithmeticException if the token count exceeds Integer.MAX_VALUE. Since this is called inside the try block that catches Exception, a hypothetical overflow would abort the entire exchange even though the API call itself succeeded.

More importantly, this narrowing is unnecessary. GenAiUsage accepts Long directly — the existing createChatCompletion in OpenAIProducer passes usage.promptTokens() (a long) straight to GenAiUsage.of(Long, Long, ...) without any conversion:

Suggested change
private static Integer toTokenCount(long tokens) {
private static Long toTokenCount(long tokens) {
return tokens;
}

Or just inline the long values directly and drop toTokenCount entirely, matching the OpenAIProducer pattern.

finishReason,
response.model().toString())),
() -> observation.recordSuccess(GenAiUsage.of(null, null, finishReason, response.model().toString())));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as in OpenAIEmbeddingsProducerMath.toIntExact is both unnecessary (the Long overload of GenAiUsage.of exists) and risky (throws ArithmeticException on overflow, which the catch block would treat as a failed operation).

Suggested change
}
private static Long toTokenCount(long tokens) {
return tokens;
}

.system("openai")
.requestModel(model)
.componentScheme("openai")
.build();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createResponse and createStructuredResponse are nearly identical — same observation context, same error handling, same finally. The only difference is the SDK call. Consider extracting a common helper, e.g.:

private Response observedCall(Exchange exchange, String model,
        ThrowingSupplier<Response> call) throws Exception {
    GenAiObservationContext ctx = GenAiObservationContext.builder()
            .operationName(GenAiOperationName.CHAT)
            .system("openai").requestModel(model)
            .componentScheme("openai").build();
    GenAiObservation observation = GenAiObservability.start(exchange, ctx);
    try {
        Response response = call.get();
        recordResponseSuccess(observation, response);
        return response;
    } catch (Exception e) {
        GenAiErrorSupport.apply(exchange, e);
        observation.recordError(e);
        throw e;
    } finally {
        observation.close();
    }
}

Then createResponse becomes observedCall(exchange, model, () -> getEndpoint().getClient().responses().create(params)) and createStructuredResponse becomes observedCall(exchange, model, () -> getEndpoint().getClient().responses().create(structuredParams).rawResponse()).

Not blocking, but reduces ~40 lines of duplication to ~2.

observation.recordError(e);
throw e;
} finally {
observation.close();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment that was here ("this operation is used to gate untrusted content, so a missing verdict must fail the exchange...") explained a security invariant — why a mismatched result count throws rather than silently proceeding. Worth keeping; it's not redundant with the observability changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants