mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-20 10:52:08 +00:00
e36945741e
Small Ollama models (qwen2.5:11b, qwen2.5:14b, similar) frequently emit
Pulse tool invocations as plain JSON inside content instead of routing
through the structured tool_calls channel. Users saw raw payloads like
`{"name": "pulse_query", "parameters": {...}}` as the assistant's final
response.
Extend cleanToolCallArtifacts and containsToolCallMarker with a new pass
that detects this leak shape, gated on a closed allowlist of canonical
tool names sourced from the runtime registry. The allowlist auto-syncs
when registerTools() gains a tool, so no separate hand-maintained list.
Anchored on (?:^|\n) and a leading `"name"` key so prose containing JSON
fragments, unrelated objects (`{"foo":"bar"}`), or named resources
(`{"name":"my-vm","cpu":50}`) are left untouched.
47 lines
1.3 KiB
Go
47 lines
1.3 KiB
Go
package tools
|
|
|
|
import "sync"
|
|
|
|
// KnownToolNames returns the canonical list of registered Pulse tool names,
|
|
// keyed off the registry built by registerTools(). The list is built lazily
|
|
// on first call via a throwaway executor so adding a new tool to
|
|
// registerTools() automatically extends the allowlist — no separate
|
|
// hand-maintained list to drift out of sync.
|
|
func KnownToolNames() []string {
|
|
initKnownToolNames()
|
|
out := make([]string, len(knownToolNamesList))
|
|
copy(out, knownToolNamesList)
|
|
return out
|
|
}
|
|
|
|
// IsKnownToolName reports whether name is one of the canonical Pulse tool
|
|
// names. Used by chat-content sanitisers to gate stripping on a closed
|
|
// allowlist (e.g. JSON tool-call shapes leaked by weak local models that
|
|
// route the call into content instead of through the tool_calls channel).
|
|
func IsKnownToolName(name string) bool {
|
|
if name == "" {
|
|
return false
|
|
}
|
|
initKnownToolNames()
|
|
_, ok := knownToolNamesSet[name]
|
|
return ok
|
|
}
|
|
|
|
var (
|
|
knownToolNamesOnce sync.Once
|
|
knownToolNamesList []string
|
|
knownToolNamesSet map[string]struct{}
|
|
)
|
|
|
|
func initKnownToolNames() {
|
|
knownToolNamesOnce.Do(func() {
|
|
e := NewPulseToolExecutor(ExecutorConfig{})
|
|
names := e.registry.allNames()
|
|
knownToolNamesList = names
|
|
knownToolNamesSet = make(map[string]struct{}, len(names))
|
|
for _, n := range names {
|
|
knownToolNamesSet[n] = struct{}{}
|
|
}
|
|
})
|
|
}
|