HsbcJone commented on code in PR #1352: URL: https://github.com/apache/flink-agents/pull/1352#discussion_r4488701743
########## api/src/main/java/org/apache/flink/agents/api/event/UserDefinedContext.java: ########## @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.agents.api.event; + +import org.apache.flink.agents.api.Event; + +import javax.annotation.Nullable; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Optional opaque application data carried on a built-in request and echoed on its response. + * + * <p>The value is not interpreted, not injected into prompts or tool arguments, and omitted + * entirely when absent so existing events keep their serialized shape. + * + * <p>Maps and lists, including nested ones, are copied when attached. A later change to the + * caller's object does not change an event that already holds the value. Other values are stored as + * given. + */ +final class UserDefinedContext { + + static final String ATTRIBUTE = "context"; + + private UserDefinedContext() {} + + static void set(Event event, @Nullable Object context) { + if (context == null) { + event.getAttributes().remove(ATTRIBUTE); + } else { + event.setAttr(ATTRIBUTE, snapshot(context)); + } + } + + /** + * Returns a detached copy of {@code value}. Maps and lists are copied recursively so the event + * does not share the caller's mutable structure. + */ + static Object snapshot(Object value) { + if (value instanceof Map) { + Map<?, ?> map = (Map<?, ?>) value; + Map<Object, Object> copy = new LinkedHashMap<>(); + for (Map.Entry<?, ?> entry : map.entrySet()) { + copy.put(snapshot(entry.getKey()), snapshot(entry.getValue())); + } + return copy; + } + if (value instanceof List) { + List<?> list = (List<?>) value; + List<Object> copy = new ArrayList<>(list.size()); + for (Object item : list) { + copy.add(snapshot(item)); + } + return copy; + } + return value; Review Comment: Done in `e963cca`: `UserDefinedContext.snapshot` recursively copies object/primitive arrays (including nested in lists). Covered by `nestedArrayIsCopiedSoLaterMutationDoesNotChangeAnEarlierRequest` (`String[]`, `int[]`, nested `String[]`). ########## api/src/main/java/org/apache/flink/agents/api/event/ChatRequestEvent.java: ########## @@ -54,20 +56,31 @@ public class ChatRequestEvent extends Event { ChatMessage.class, Map.class), BuiltInAttribute.optional("prompt_args", Map.class), - BuiltInAttribute.optionalUntyped("output_schema")); + BuiltInAttribute.optionalUntyped("output_schema"), + BuiltInAttribute.optionalUntyped(UserDefinedContext.ATTRIBUTE)); public ChatRequestEvent( String model, List<ChatMessage> messages, @Nullable Map<String, Object> promptArgs, - @Nullable Object outputSchema) { + @Nullable Object outputSchema, + @Nullable Object context) { Review Comment: Done in `e963cca`: all eight request/response events use `userContext` / Python `user_context` (key `user_context`; accessors `getUserContext()` / `user_context`). ########## api/src/main/java/org/apache/flink/agents/api/event/ChatRequestEvent.java: ########## @@ -145,4 +158,20 @@ public Map<String, Object> getPromptArgs() { Map<String, Object> args = (Map<String, Object>) getAttr("prompt_args"); return args != null ? args : Collections.emptyMap(); } + + /** + * Attaches opaque application data to echo unchanged on the matching {@link ChatResponseEvent}. + * The value is not added to messages or prompt arguments. {@code null} omits it. + */ + public ChatRequestEvent withContext(@Nullable Object context) { Review Comment: Done in `e963cca`: request events take the map only in the constructor; `withUserContext()` remains on the four response events. ########## docs/content/docs/development/calling-llms/chat.md: ########## @@ -107,6 +107,8 @@ InputEvent → request Action → ChatRequestEvent A `ChatRequestEvent` can name a model router instead of a chat model. The built-in action selects the model first and emits a `ModelRoutingEvent`; see [Model Routing]({{< ref "docs/development/calling-llms/model_routing" >}}). +Pass optional `context` (Python) or `withContext` (Java) when one Action sends several chat requests and the response Action needs to tell them apart. The value is returned unchanged on the `ChatResponseEvent`, including after tool-call rounds, retries, model fallback, and a failed response. It is not added to the prompt. See [Request context]({{< ref "docs/development/create-agent/events#request-context" >}}). Review Comment: Done in `e963cca`: `chat.md` now describes app-defined data request→response and links Serialization/Typed Values and User context. ########## docs/content/docs/development/create-agent/events.md: ########## @@ -278,3 +278,53 @@ Event that your Actions can handle to continue the workflow: | Call tools | `ToolRequestEvent`, `ToolResponseEvent` | [Tool Use]({{< ref "docs/development/calling-llms/tool_use#built-in-events-and-actions" >}}) | | Retrieve context | `ContextRetrievalRequestEvent`, `ContextRetrievalResponseEvent` | [Vector Stores]({{< ref "docs/development/vector-search/vector_stores#built-in-events-and-actions" >}}) | | Call a decision model | `DecisionRequestEvent`, `DecisionResponseEvent` | [Decision Models]({{< ref "docs/development/decision_models#event-driven-calls" >}}) | + +### Request context + +Chat, tool, context-retrieval, and decision request events accept an optional `context` +value: a map or other JSON-compatible object your Action uses to tell responses apart. +The built-in Action copies that value onto the matching response and does not add it to +the prompt, tool arguments, or vector-store query. Omitting it leaves the response +without a `context` attribute, which is what existing callers do. + +The value is copied when the event is created, including nested maps and lists, so a +later change to the object you passed in does not change the event. It round-trips as Review Comment: Done in `e963cca`: points at Serialization/Typed Values; notes JSON may change runtime types; construction copies mutable structure only. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
