Skip to content

Commit df03967

Browse files
authored
feat: Add preload frontmatters skill source proxy. (#748)
Given instructions and resource files can be large, it might turn out to be expensive to load them all to RAM. Fortunately, at this stage of skill toolset implementation, instructions and resources are most likely going to be read just once - and then they'll stay in the conversation history. Frontmatters, on the other hand, are going to be listed frequently, and frontmatters are relatively lightweight, thus a proxy caching only the frontmatters (as opposed to the full preload) can help reach maximum benefit.
1 parent b89683b commit df03967

2 files changed

Lines changed: 272 additions & 0 deletions

File tree

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package skill
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"slices"
21+
"strings"
22+
"sync"
23+
)
24+
25+
type frontmatterPreloadSource struct {
26+
Source
27+
mu sync.RWMutex
28+
frontmatters map[string]*Frontmatter // for fast lookup.
29+
sorted []*Frontmatter // for deterministic listing.
30+
}
31+
32+
// WithFrontmatterPreloadSource returns a Source proxy that wraps the given
33+
// source and eagerly preloads all skill Frontmatters into memory upon creation.
34+
// This optimizes future calls to ListFrontmatters and LoadFrontmatter.
35+
// Additionally, 'reload' callback is returned: calling it actualizes
36+
// data by loading frontmatters anew.
37+
func WithFrontmatterPreloadSource(ctx context.Context, source Source) (Source, func(context.Context) error, error) {
38+
s := &frontmatterPreloadSource{Source: source}
39+
if err := s.reload(ctx); err != nil {
40+
return nil, nil, err
41+
}
42+
return s, s.reload, nil
43+
}
44+
45+
func (s *frontmatterPreloadSource) ListFrontmatters(ctx context.Context) ([]*Frontmatter, error) {
46+
s.mu.RLock()
47+
defer s.mu.RUnlock()
48+
return s.sorted, nil
49+
}
50+
51+
func (s *frontmatterPreloadSource) LoadFrontmatter(ctx context.Context, name string) (*Frontmatter, error) {
52+
s.mu.RLock()
53+
defer s.mu.RUnlock()
54+
frontmatter, exists := s.frontmatters[name]
55+
if !exists {
56+
return nil, ErrSkillNotFound
57+
}
58+
return frontmatter, nil
59+
}
60+
61+
func (s *frontmatterPreloadSource) reload(ctx context.Context) error {
62+
sorted, err := s.Source.ListFrontmatters(ctx)
63+
if err != nil {
64+
return fmt.Errorf("preload: %w", err)
65+
}
66+
sorted = slices.Clone(sorted)
67+
slices.SortFunc(sorted, func(lhs, rhs *Frontmatter) int {
68+
return strings.Compare(lhs.Name, rhs.Name)
69+
})
70+
71+
frontmatters := make(map[string]*Frontmatter, len(sorted))
72+
for _, frontmatter := range sorted {
73+
if _, exists := frontmatters[frontmatter.Name]; exists {
74+
return fmt.Errorf("%w: %q", ErrDuplicateSkill, frontmatter.Name)
75+
}
76+
frontmatters[frontmatter.Name] = frontmatter
77+
}
78+
79+
s.mu.Lock()
80+
s.frontmatters = frontmatters
81+
s.sorted = sorted
82+
s.mu.Unlock()
83+
return nil
84+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package skill
16+
17+
import (
18+
"context"
19+
"errors"
20+
"testing"
21+
22+
"github.com/google/go-cmp/cmp"
23+
)
24+
25+
func TestWithFrontmatterPreloadSource_Success(t *testing.T) {
26+
// Test Init.
27+
mock := &mockFrontmatterPreloadBaseSource{
28+
frontmatters: []*Frontmatter{{Name: "B"}, {Name: "A"}},
29+
}
30+
source, reload, err := WithFrontmatterPreloadSource(t.Context(), mock)
31+
if err != nil {
32+
t.Fatalf("WithFrontmatterPreloadSource() returned unexpected error: %v", err)
33+
}
34+
if got, want := mock.listFrontmattersCalls, 1; got != want {
35+
t.Errorf("mock.listFrontmattersCalls = %v, want %v", got, want)
36+
}
37+
got, err := source.ListFrontmatters(t.Context())
38+
if err != nil {
39+
t.Fatalf("source.ListFrontmatters() returned unexpected error: %v", err)
40+
}
41+
want := []*Frontmatter{{Name: "A"}, {Name: "B"}}
42+
if diff := cmp.Diff(want, got); diff != "" {
43+
t.Errorf("ListFrontmatters mismatch (-want +got):\n%s", diff)
44+
}
45+
46+
// Test Reload.
47+
mock.frontmatters = []*Frontmatter{{Name: "C"}}
48+
err = reload(t.Context())
49+
if err != nil {
50+
t.Fatalf("reload() returned unexpected error: %v", err)
51+
}
52+
if got, want := mock.listFrontmattersCalls, 2; got != want {
53+
t.Errorf("mock.listFrontmattersCalls = %v, want %v after reload", got, want)
54+
}
55+
got, err = source.ListFrontmatters(t.Context())
56+
if err != nil {
57+
t.Fatalf("source.ListFrontmatters() returned unexpected error: %v", err)
58+
}
59+
want = []*Frontmatter{{Name: "C"}}
60+
if diff := cmp.Diff(want, got); diff != "" {
61+
t.Errorf("ListFrontmatters mismatch after reload (-want +got):\n%s", diff)
62+
}
63+
}
64+
65+
func TestWithFrontmatterPreloadSource_DuplicateSkill(t *testing.T) {
66+
mock := &mockFrontmatterPreloadBaseSource{
67+
frontmatters: []*Frontmatter{{Name: "A"}, {Name: "A"}},
68+
}
69+
70+
_, _, err := WithFrontmatterPreloadSource(t.Context(), mock)
71+
72+
if !errors.Is(err, ErrDuplicateSkill) {
73+
t.Errorf("WithFrontmatterPreloadSource error = %v, want %v", err, ErrDuplicateSkill)
74+
}
75+
}
76+
77+
func TestWithFrontmatterPreloadSource_ReloadFailureDoesNotInvalidateCache(t *testing.T) {
78+
mock := &mockFrontmatterPreloadBaseSource{frontmatters: []*Frontmatter{{Name: "A"}}}
79+
source, reload, err := WithFrontmatterPreloadSource(t.Context(), mock)
80+
if err != nil {
81+
t.Fatalf("WithFrontmatterPreloadSource failed: %v", err)
82+
}
83+
testErr := errors.New("reload error")
84+
mock.listFrontmattersErr = testErr
85+
86+
err = reload(t.Context())
87+
88+
// If reload fails, the cache should still be valid.
89+
if !errors.Is(err, testErr) {
90+
t.Fatalf("Reload error = %v, want %v", err, testErr)
91+
}
92+
got, err := source.ListFrontmatters(t.Context())
93+
if err != nil {
94+
t.Fatalf("ListFrontmatters failed: %v", err)
95+
}
96+
if diff := cmp.Diff([]*Frontmatter{{Name: "A"}}, got); diff != "" {
97+
t.Errorf("ListFrontmatters mismatch (-want +got):\n%s", diff)
98+
}
99+
}
100+
101+
func TestFrontmatterPreloadSource_ListFrontmatters(t *testing.T) {
102+
mock := &mockFrontmatterPreloadBaseSource{frontmatters: []*Frontmatter{{Name: "A"}}}
103+
104+
source, _, err := WithFrontmatterPreloadSource(t.Context(), mock)
105+
if err != nil {
106+
t.Fatalf("WithFrontmatterPreloadSource failed: %v", err)
107+
}
108+
109+
// Should be called once on init.
110+
if got, want := mock.listFrontmattersCalls, 1; got != want {
111+
t.Errorf("mock.listFrontmattersCalls = %v, want %v", got, want)
112+
}
113+
// Calls should be cached.
114+
got1, err := source.ListFrontmatters(t.Context())
115+
if err != nil {
116+
t.Fatalf("ListFrontmatters failed: %v", err)
117+
}
118+
got2, err := source.ListFrontmatters(t.Context())
119+
if err != nil {
120+
t.Fatalf("ListFrontmatters failed: %v", err)
121+
}
122+
// Should not change since list is cached.
123+
if got, want := mock.listFrontmattersCalls, 1; got != want {
124+
t.Errorf("mock.listFrontmattersCalls = %v, want %v", got, want)
125+
}
126+
if diff := cmp.Diff(got1, got2); diff != "" {
127+
t.Errorf("ListFrontmatters results differ (-got1 +got2):\n%s", diff)
128+
}
129+
}
130+
131+
func TestFrontmatterPreloadSource_LoadFrontmatter(t *testing.T) {
132+
mock := &mockFrontmatterPreloadBaseSource{
133+
frontmatters: []*Frontmatter{{Name: "A", Description: "desc A"}},
134+
}
135+
136+
source, _, err := WithFrontmatterPreloadSource(t.Context(), mock)
137+
if err != nil {
138+
t.Fatalf("WithFrontmatterPreloadSource failed: %v", err)
139+
}
140+
141+
// Success case.
142+
got, err := source.LoadFrontmatter(t.Context(), "A")
143+
if err != nil {
144+
t.Fatalf("source.LoadFrontmatter() returned unexpected error: %v", err)
145+
}
146+
want := &Frontmatter{Name: "A", Description: "desc A"}
147+
if diff := cmp.Diff(want, got); diff != "" {
148+
t.Errorf("LoadFrontmatter mismatch (-want +got):\n%s", diff)
149+
}
150+
151+
// NotFound case.
152+
_, err = source.LoadFrontmatter(t.Context(), "B")
153+
if !errors.Is(err, ErrSkillNotFound) {
154+
t.Errorf("LoadFrontmatter error = %v, want %v", err, ErrSkillNotFound)
155+
}
156+
157+
// Check LoadFrontmatter was not called for existing or non-existing skill -
158+
// they were preloaded.
159+
if got, want := mock.loadFrontmatterCalls, 0; got != want {
160+
t.Errorf("mock.loadFrontmatterCalls = %v, want %v (cached)", got, want)
161+
}
162+
}
163+
164+
type mockFrontmatterPreloadBaseSource struct {
165+
Source
166+
frontmatters []*Frontmatter
167+
listFrontmattersErr error
168+
listFrontmattersCalls int
169+
loadFrontmatterCalls int
170+
}
171+
172+
func (m *mockFrontmatterPreloadBaseSource) ListFrontmatters(ctx context.Context) ([]*Frontmatter, error) {
173+
m.listFrontmattersCalls++
174+
if m.listFrontmattersErr != nil {
175+
return nil, m.listFrontmattersErr
176+
}
177+
return m.frontmatters, nil
178+
}
179+
180+
func (m *mockFrontmatterPreloadBaseSource) LoadFrontmatter(ctx context.Context, name string) (*Frontmatter, error) {
181+
m.loadFrontmatterCalls++
182+
for _, fm := range m.frontmatters {
183+
if fm.Name == name {
184+
return fm, nil
185+
}
186+
}
187+
return nil, ErrSkillNotFound
188+
}

0 commit comments

Comments
 (0)