58 lines
1.7 KiB
Go
58 lines
1.7 KiB
Go
package agent
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/ollama/ollama/api"
|
|
)
|
|
|
|
// activateSkill loads opts.SkillName from the catalog and injects a synthetic
|
|
// assistant tool call plus tool result before the first model request, so the
|
|
// transcript looks like a real skill tool invocation. It emits the same
|
|
// tool_call_detected -> tool_started -> tool_finished lifecycle the model path
|
|
// uses, and returns the messages to prepend. A blank SkillName is a no-op.
|
|
func (s *Session) activateSkill(ctx context.Context, runID string, opts RunOptions) ([]api.Message, error) {
|
|
name := strings.TrimSpace(opts.SkillName)
|
|
if name == "" {
|
|
return nil, nil
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
default:
|
|
}
|
|
skill, err := s.Skills.Load(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
args := api.NewToolCallFunctionArguments()
|
|
args.Set("name", skill.Name)
|
|
call := api.ToolCall{
|
|
ID: "call_skill_" + uuid.NewString(),
|
|
Function: api.ToolCallFunction{Name: "skill", Arguments: args},
|
|
}
|
|
result := api.Message{
|
|
Role: "tool",
|
|
ToolName: "skill",
|
|
ToolCallID: call.ID,
|
|
Content: skill.Content(),
|
|
}
|
|
meta := newEventMetadata(runID, opts)
|
|
if err := s.emit(newToolCallDetected(meta, []api.ToolCall{call})); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.emit(newToolStarted(meta, call.ID, "skill", s.currentWorkingDir(), args.ToMap())); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.emitIgnoringCanceled(ctx, newToolFinished(meta, ToolStatusDone, call.ID, "skill", s.currentWorkingDir(), args.ToMap(), result.Content, "")); err != nil {
|
|
return nil, err
|
|
}
|
|
return []api.Message{
|
|
{Role: "assistant", ToolCalls: []api.ToolCall{call}},
|
|
result,
|
|
}, nil
|
|
}
|