mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
fix(boot): a rebuilt dispatcher keeps the missing-sidecar notice
stageDispatcher built the successor with empty options, so after a subgraph rebuild an optional extension without a sidecar was skipped silently, right at the moment an install or enable makes that most likely. The successor now takes the reporting of the dispatcher it replaces. Refs #12258
This commit is contained in:
1 parent
268b955eab
commit
4d3ea82f67
3 files changed
+59
-1
No files matched your search
@@ -8,6 +8,8 @@ import (
|
||||
"reasonix/internal/contract/event"
|
||||
"reasonix/internal/ext/extension"
|
||||
"reasonix/internal/ext/extension/dispatch"
|
||||
"reasonix/internal/ext/extension/protocol"
|
||||
"reasonix/internal/ext/extension/sidecar"
|
||||
)
|
||||
|
||||
// The dispatcher the stage's hook feeds reaches the frontend sink as one typed
|
||||
@@ -41,3 +43,49 @@ func TestMissingSidecarReachesTheSinkAsATypedNotice(t *testing.T) {
|
||||
t.Fatalf("payload = %+v (ok=%v), want %+v", got, ok, want)
|
||||
}
|
||||
}
|
||||
|
||||
// A dispatcher staged by a subgraph rebuild keeps the stage's reporting, so a
|
||||
// sidecar missing right after an extension was installed still reaches the sink.
|
||||
func TestRebuiltDispatcherKeepsTheMissingSidecarNotice(t *testing.T) {
|
||||
sink := ¬iceSink{}
|
||||
stage, err := startExtensions(context.Background(), Options{}, config.RootsForHome(t.TempDir()), t.TempDir(), nil, sink)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
point := extension.PointToolBefore
|
||||
builder := extension.NewBuilder().WithSystemPrompt("p").WithGeneration(2)
|
||||
builder.AddContributor(extension.ContributorFunc{
|
||||
ContributorName: "rebuilt",
|
||||
Fn: func(context.Context) ([]extension.Contribution, error) {
|
||||
return []extension.Contribution{{
|
||||
Kind: extension.KindInterceptor, ID: string(point),
|
||||
Source: extension.ContributionSource{Scope: extension.ScopePlugin, PluginID: "aipush-ask-bridge", Origin: "plugin"},
|
||||
}}, nil
|
||||
},
|
||||
})
|
||||
snap, set, err := builder.Build(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = set.Close() })
|
||||
previous := dispatch.New(nil, nil, func(string) dispatch.Client { return nil }, nil, dispatch.Options{
|
||||
Warn: stage.warn, SidecarDown: stage.sidecarDown,
|
||||
})
|
||||
mgr, _, err := sidecar.StartPackagesWithPlan(context.Background(), robustTempDir(t), protocol.SessionContext{SessionID: "s", WorkspaceRoot: robustTempDir(t), Generation: 2}, nil, nil, &extension.RuntimePlan{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = mgr.Close() })
|
||||
res := &BuildResult{Snapshot: snap, Dispatcher: previous, Extensions: mgr}
|
||||
if err := stageDispatcher(res); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for range 2 {
|
||||
if _, err := res.Dispatcher.Intercept(context.Background(), point, &dispatch.ToolBeforePayload{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if len(sink.ev) != 1 || sink.ev[0].Code != event.NoticeCodeExtensionSkipped {
|
||||
t.Fatalf("notices = %+v, want exactly one %q notice from the rebuilt dispatcher", sink.ev, event.NoticeCodeExtensionSkipped)
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,11 @@ func stageDispatcher(res *BuildResult) error {
|
||||
}
|
||||
clients := sidecarClientResolver(res.Extensions)
|
||||
required := requiredRuntimeSet(res.Extensions)
|
||||
res.Dispatcher = dispatch.New(res.Snapshot.InterceptorChain(), res.Snapshot.Replacements(), clients, required, dispatch.Options{})
|
||||
var opts dispatch.Options
|
||||
if res.Dispatcher != nil {
|
||||
opts = res.Dispatcher.Options()
|
||||
}
|
||||
res.Dispatcher = dispatch.New(res.Snapshot.InterceptorChain(), res.Snapshot.Replacements(), clients, required, opts)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -175,6 +175,12 @@ func New(chain map[extension.InterceptorPoint][]extension.Contribution, replacem
|
||||
}
|
||||
}
|
||||
|
||||
// Options returns the reporting this dispatcher was built with, so a successor
|
||||
// built after a rebuild reports through the same channels.
|
||||
func (d *Dispatcher) Options() Options {
|
||||
return Options{Warn: d.warn, SidecarDown: d.sidecarDown}
|
||||
}
|
||||
|
||||
// Intercept walks the chain for point in frozen order, calling each
|
||||
// plugin-backed interceptor with the current (possibly already replaced)
|
||||
// payload. payloadPtr must be a pointer to the point's registered DTO; on
|
||||
|
||||
Reference in new issue
Block a user