feat(capability/skills): 可复用工作流技能(Skill 模型 + Store + YAML Loader + Runner 展开 + 注册)
This commit is contained in:
1 parent
d947ec41a9
commit
915aa8a100
10 files changed
+1235
No files matched your search
@@ -0,0 +1,68 @@
|
||||
package skills
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/db"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/pkg/plugins/sdk"
|
||||
)
|
||||
|
||||
// testLogger returns a logger that writes nowhere, keeping test output clean.
|
||||
func testLogger() *slog.Logger {
|
||||
return slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
}
|
||||
|
||||
// makeSkill builds a deterministic skill populated with one step, one ref, and
|
||||
// one guardrail, so tests can assert the full round trip.
|
||||
func makeSkill(t *testing.T, name string) *Skill {
|
||||
t.Helper()
|
||||
sk, err := New(name, "desc "+name, "prompt "+name,
|
||||
[]sdk.Step{{Name: "s1", Prompt: "step prompt"}},
|
||||
[]sdk.Ref{{Name: "r1", Type: "tool", Value: "v1", Optional: false}},
|
||||
[]sdk.Guardrail{{ID: "g1", Message: "msg", Enforced: true}},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("New(%q): %v", name, err)
|
||||
}
|
||||
return sk
|
||||
}
|
||||
|
||||
// mockSkill is a minimal sdk.Skill used to feed invalid inputs (blank name) and
|
||||
// to exercise the store's generic sdk.Skill handling.
|
||||
type mockSkill struct{ name string }
|
||||
|
||||
func (m mockSkill) Name() string { return m.name }
|
||||
func (m mockSkill) Description() string { return "desc" }
|
||||
func (m mockSkill) Prompt() string { return "prompt" }
|
||||
func (m mockSkill) Steps() []sdk.Step { return nil }
|
||||
func (m mockSkill) Refs() []sdk.Ref { return nil }
|
||||
func (m mockSkill) Guardrails() []sdk.Guardrail { return nil }
|
||||
|
||||
// newTestStore opens an isolated in-memory SQLite database and builds a Store
|
||||
// over it. It is cleaned up when the test finishes.
|
||||
func newTestStore(t *testing.T) *Store {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
database, err := db.Open(ctx, ":memory:", testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("db.Open(:memory:): %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = database.Close() })
|
||||
store, err := NewStore(ctx, database, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("NewStore: %v", err)
|
||||
}
|
||||
return store
|
||||
}
|
||||
|
||||
// writeFile writes content to path, failing the test on error.
|
||||
func writeFile(t *testing.T, path, content string) {
|
||||
t.Helper()
|
||||
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
||||
t.Fatalf("write %s: %v", path, err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user