Skip to content

chore: Refactor ORD resolution - #43

Draft
mlakov wants to merge 1 commit into
mainfrom
chore/refactor-ord-resolution
Draft

mlakov wants to merge 1 commit into
mainfrom
chore/refactor-ord-resolution

Conversation

@mlakov

@mlakov mlakov commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Refactor ORD Resource Resolution with Extensible Resolvers

Refactor

♻️ Replaced the centralized ORD resource provider with service-loaded resolvers that select static or dynamic resources based on the requested resource and perspective. ORD document processing is now split into dedicated node customizers and generators, improving extensibility and separation of concerns.

Changes

  • resolver/*: Added the OrdResolver extension point and dedicated resolvers for static/dynamic ORD documents and resource definitions.
  • DocumentsServlet.java: Loads applicable resolvers, selects them by priority, defaults missing perspectives to system-version, and returns 404 when no resolver applies.
  • OrdJsonInputStream.java: Replaced generic node processors with context-aware customizers and generators, supporting generated content at matching JSON structure boundaries.
  • common/customizers/*: Introduced the customizer API and migrated entry-point and resource-definition transformations to it.
  • common/generators/CdsOrdNodeGenerator.java: Added an extension point for generating ORD nodes from the JSON stream context.
  • MtxSidecarClient*: Changed sidecar responses from strings to raw byte arrays to preserve resource content without unnecessary encoding conversion.
  • Utils.java: Updated ORD stream construction for generators/customizers and made iterator conversion null-safe.
  • META-INF/services/*: Registered resolvers and customizers through the service loader; removed obsolete provider and processor registrations.
  • Removed the legacy OrdResourcesProvider implementations, configuration, and CdsOrdNodeProcessor.
  • Updated servlet, sidecar client, JSON stream, and customizer tests for the new resolver architecture and binary response handling.
  • 🔄 Regenerate and Update Summary
PR Bot Information

Version: 1.32.6

  • File Content Strategy: Full file content
  • Output Template: Default Template
  • Event Trigger: pull_request.opened
  • Summary Prompt: Default Prompt
  • Correlation ID: 8b041230-c3db-11f1-9dca-be85dcdcc9bd
  • LLM: gpt-5.6-sol

@mlakov
mlakov force-pushed the chore/refactor-ord-resolution branch from 22813f9 to db941de Compare October 9, 2026 12:36
@mlakov
mlakov force-pushed the chore/refactor-ord-resolution branch from db941de to 4ee3cc1 Compare October 9, 2026 20:24
import com.sap.cds.services.runtime.CdsRuntimeAware;
import java.io.InputStream;

public interface OrdResolver extends CdsRuntimeAware {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

What is the reason to go away from the CdsProvider approach? This is common practise in CAP Java in regards to extensibility and plug-ability.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The idea behind the OrdResovler interface is to allow for granular control over the exposure of ORD resources. E.g. if one wants to register a custom resolver for an agent - this can happen in complete isolation from all other resolvers.

import com.sap.cds.services.runtime.CdsRuntimeAware;
import java.util.function.Predicate;

public interface CdsOrdNodeCustomizer extends CdsRuntimeAware {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If I understand correctly, CdsOrdNodeCustomizer is for modifying content while CdsOrdNodeGenerator is for contributing new things, correct?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Correct. The customizers can only modify existing nodes in the JSON tree. Generators - on the contrary - they provide new nodes on arbitrary positions in the document.

* @param context the JSON stream context
* @return the generated node as a string
*/
String generate(JsonStreamContext context);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Taking the following snippet as what is required to be added for agents, would you see that all the content is contributed via this interface? Or would it just be the agent card?

I´m wondering how e.g. the "partOfPackage" could be calculated correctly based on the given JsonStreamContext. Ideally, we can come up with an implementation idea for this given snippet.

{
  "apiResources": [
    {
      "ordId": "sap.agtpocxtrv:apiResource:TravelAgent:v1",
      "title": "Travel Agent",
      "shortDescription": "Short description of Travel Agent",
      "description": "Long description of Travel Agent",
      "version": "1.0.8",
      "lastUpdate": "2026-09-22T09:48:00Z",
      "apiProtocol": "a2a",
      "releaseStatus": "beta",
      "partOfPackage": "sap.agtpocxtrv:package:capirextravelsjava-api:v1",
      "visibility": "internal",
      "extensible": {"supported": "no"},
      "resourceDefinitions": [
        {
          "type": "a2a-agent-card",
          "mediaType": "application/json",
          "url": "/a2a/travel/.well-known/agent-card.json",
          "accessStrategies": [{ "type": "open" }]
        }
      ],
      "exposedEntityTypes": [
      ],
      "tags": ["travel", "ai-agent", "a2a"]
    }
  ],
  "agents": [
    {
      "ordId": "sap.agtpocxtrv:agent:travelAgent:v1",
      "title": "Travel Agent",
      "shortDescription": "AI agent specialized in travel woizh cuetom conettna",
      "description": "A longer description of this Agent with **markdown**...",
      "version": "1.0.8",
      "visibility": "public",
      "releaseStatus": "active",
      "lastUpdate": "2026-09-22T09:48:00Z",
      "partOfPackage": "sap.agtpocxtrv:package:capirextravelsjava-api:v1",
      "relatedEntityTypes": [
      ],

      "exposedApiResources": [
        {"ordId": "sap.agtpocxtrv:apiResource:TravelAgent:v1"}
      ],

      "integrationDependencies": [
      ],

      "partOfGroups": [],
      "labels": {
        "sap.ai:interactionMode": ["conversational", "system-triggered"],
        "sap.joule:scenario.mapping": ["agent"]
      },
      "tags": ["travel", "ai-agent", "a2a"]
    }
  ]
}

@mlakov mlakov Oct 13, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a very valid question. Let's discuss what makes sense in this case - either the generator needs to inspect its environment or we need to change the interface so that the generator consumes some sort of ORD context.

In the current implementation where CDS compilation is responsible for generating the ORD metadata these relationships can be expressed via specific CDS annotations. I don't think that this can be considered applicable for the current approach... unless the Agent plugin begins understanding these annotations 🤔

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The cds model for agents look like this:

@agent @odata @path: 'travel' service TravelService {
...
}

The @agent annotation will trigger the addition of an agents node in the metadata plus an apiResources with a resourceDefinitions of type a2a-agent-card. The url should be derivable from the model. Maybe node and java differ in some prefix. But I don´t see a reason, why this skeleton of the metadata cannot be added at build time already. Model wise this is stable. Maybe the current approach goes too far to also generate the metadata of the ord document.

When it comes to the content of the agent card, it will be different. This is calculated at runtime and might be called by ord through a service to serve the agent card in the context of ord.

Let´s discuss this.

@mlakov mlakov Oct 16, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is why the OrdResolver concept is introduced. It allows for granular control over the contents of each ORD resource, e.g. the content of the agent card itself.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants