agentscope-ai / agentscope-ai/agentscope-java

[Feature]: Support passing custom context from InboundMessage to RuntimeContext

Abierto
#1,966 0 comentarios 0 reacciones 0 asignados Ver en GitHub
area/core/agent enhancement
Lenguaje dominante
Java
Estrellas
5.6k
Forks
1.3k
Merge medio
4 d 12 h
PR fusionados (30 d)
77

Descripción

When building a multi-tenant application with AgentScope, we need to pass business-specific parameters (like `modelConfigId`, `datasourceId`, `tenantId`) from the Controller layer all the way down to the Middleware/Tool layer via `RuntimeContext`.

Currently, `InboundMessage` has no mechanism to carry custom key-value pairs, and `ChannelRouter.buildContext` only puts `agentId` into `MsgContext.extra`. This forces us to use `ConcurrentHashMap` with `userId` as the key to simulate context passing:

```java
// Workaround: Store in ConcurrentHashMap before dispatch
modelContext.put(userId, modelConfigId);
datasourceContext.put(userId, datasourceId);

// Workaround: Retrieve in Gateway
private void attachModelContext(RuntimeContext.Builder builder, String userId) {
String modelConfigId = modelContext.resolve(userId);
if (modelConfigId != null) {
builder.put("modelConfigId", modelConfigId);
}
}

// Workaround: Clean up after dispatch
modelContext.remove(userId);
datasourceContext.remove(userId);
```

This approach has several issues:
1. **Manual lifecycle management** - Must remember to `put` before and `remove` after each call
2. **Memory leak risk** - If `remove` is missed in error paths, entries accumulate
3. **Thread safety concerns** - `put` and `remove` are not atomic
4. **Unclear data flow** - Context goes through an intermediate ConcurrentHashMap instead of flowing directly

## Describe the solution you'd like

I suggest adding an `extra` field to `InboundMessage` (or similar mechanism) to allow callers to pass custom context that flows through the chain:

```
InboundMessage.extra → MsgContext.extra → Gateway → RuntimeContext
```

**Option A: Add `extra` to InboundMessage**

```java
public record InboundMessage(
String channelId,
String accountId,
Peer peer,
String senderId,
Peer parentPeer,
String guild,
String team,
Set roles,
List messages,
String preferredAgentId,
Map extra // ← New field
) {
// ...
}
```

Then `ChannelRouter.buildContext` would merge it:

```java
private MsgContext buildContext(InboundMessage msg, DmScope dmScope, String agentId, String userId) {
Map extra = new HashMap<>();
extra.put("agentId", agentId);
if (msg.extra() != null) {
extra.putAll(msg.extra()); // ← Merge InboundMessage.extra
}
// ... rest of logic
}
```

**Option B: Add `extra` to MsgContext builder in ChannelRouter**

Allow `ChannelRouter` to accept a context enrichment callback:

```java
public interface ChannelRouter {
RouteResult resolveRoute(InboundMessage msg, BiConsumer extraEnricher);
}
```

## Describe alternatives you've considered

1. **ThreadLocal / RequestAttributes** - Couples Gateway to Servlet API, not suitable for non-web scenarios
2. **Storing in Msg.accountId** - Overloading existing fields is hacky
3. **Custom middleware to inject context** - Requires knowing the context before dispatch, which defeats the purpose

The `ConcurrentHashMap` workaround works but is error-prone and feels like fighting the framework rather than working with it.

## Additional context

**Current architecture:**

```
Controller (extracts modelConfigId, datasourceId)

Service.put(userId, config) ← ConcurrentHashMap

Channel.dispatchStream(inbound)

Gateway.runStream(msgContext)

Gateway.get(userId) ← ConcurrentHashMap

RuntimeContext.put("modelConfigId", ...)

Middleware / Tool reads ctx.get("modelConfigId")
```

**Desired architecture:**

```
Controller (extracts modelConfigId, datasourceId)

Service.buildInbound(extra={modelConfigId, datasourceId})

Channel.dispatchStream(inbound)

Gateway.runStream(msgContext) ← MsgContext.extra contains modelConfigId

RuntimeContext.put("modelConfigId", ctx.extra().get("modelConfigId"))

Middleware / Tool reads ctx.get("modelConfigId")
```

This would eliminate the need for `ModelContext` and `DatasourceContext` helper classes entirely.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.