agentscope-ai / agentscope-ai/agentscope-java

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

Aperta
#1,966 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
area/core/agent enhancement
Lingua principale
Java
Stelle
5.6k
Fork
1.3k
Merge medio
4g 12h
PR unite (30g)
77

Descrizione

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.

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.