Skip to content

Commit 6879bbb

Browse files
Fix developer agent broken when the default OLAP connector is invalid (#8765)
1 parent f47146c commit 6879bbb

File tree

5 files changed

+778
-44
lines changed

5 files changed

+778
-44
lines changed

runtime/ai/develop_file.go

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
aiv1 "github.com/rilldata/rill/proto/gen/rill/ai/v1"
1010
"github.com/rilldata/rill/runtime"
1111
"github.com/rilldata/rill/runtime/ai/instructions"
12-
"github.com/rilldata/rill/runtime/drivers"
1312
)
1413

1514
const DevelopFileName = "develop_file"
@@ -137,37 +136,21 @@ func (t *DevelopFile) Handler(ctx context.Context, args *DevelopFileArgs) (*Deve
137136

138137
func (t *DevelopFile) userPrompt(ctx context.Context, args *DevelopFileArgs) (string, error) {
139138
// Get default OLAP info
140-
olapConnector, olapDriver, olapModeReadwrite, err := defaultOLAPInfo(ctx, t.Runtime, GetSession(ctx).InstanceID())
139+
olapInfo, err := defaultOLAPInfo(ctx, t.Runtime, GetSession(ctx).InstanceID())
141140
if err != nil {
142141
return "", err
143142
}
144143

145144
// Prepare template data.
146145
session := GetSession(ctx)
147146
data := map[string]any{
148-
"path": args.Path,
149-
"type": args.Type,
150-
"prompt": args.Prompt,
151-
"ai_instructions": session.ProjectInstructions(),
152-
"default_olap_connector": olapConnector,
153-
"default_olap_driver": olapDriver,
154-
"default_olap_readwrite": olapModeReadwrite,
147+
"path": args.Path,
148+
"type": args.Type,
149+
"prompt": args.Prompt,
150+
"ai_instructions": session.ProjectInstructions(),
151+
"default_olap_info": olapInfo,
155152
}
156153

157-
// Add OLAP dialect
158-
olap, release, err := t.Runtime.OLAP(ctx, session.InstanceID(), "")
159-
if err != nil {
160-
return "", err
161-
}
162-
defer release()
163-
dialect := olap.Dialect()
164-
if dialect == drivers.DialectUnspecified {
165-
dialect = drivers.DialectDuckDB
166-
}
167-
data["dialect"] = dialect.String()
168-
169-
// - Additional prompt: Don’t do much discovery, stay aligned on your specific task. Failure is okay, your parent will re-evaluate.
170-
171154
// Generate the user prompt
172155
return executeTemplate(`
173156
You should develop a Rill project file based on the following task description:
@@ -180,8 +163,7 @@ Here is some important context:
180163
- When you call 'write_file', if it returns a parse or reconcile error, do your best to fix the issue and try again. If you think the error is unrelated to the current path, let the parent agent know to handle it.
181164
182165
Here is some additional context that may or may not be relevant to your task:
183-
- The project's default OLAP connector is named {{ .default_olap_connector }} (driver: {{ .default_olap_driver }}).
184-
{{ if .ai_instructions }}- The user has configured global additional instructions for you. They may not relate to the current request, and may not even relate to your work as a data engineer agent. Only use them if you find them relevant. They are: {{ .ai_instructions }}
185-
{{ end }}
166+
- Info about the project's default OLAP connector: {{ .default_olap_info }}.
167+
{{ if .ai_instructions }}- The user has configured global additional instructions for you. They may not relate to the current request, and may not even relate to your work as a data engineer agent. Only use them if you find them relevant. They are: {{ .ai_instructions }}{{ end }}
186168
`, data)
187169
}

runtime/ai/developer_agent.go

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ai
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"strings"
78

@@ -121,21 +122,19 @@ func (t *DeveloperAgent) systemPrompt() (string, error) {
121122

122123
func (t *DeveloperAgent) userPrompt(ctx context.Context, args *DeveloperAgentArgs) (string, error) {
123124
// Get default OLAP info
124-
olapConnector, olapDriver, olapModeReadwrite, err := defaultOLAPInfo(ctx, t.Runtime, GetSession(ctx).InstanceID())
125+
olapInfo, err := defaultOLAPInfo(ctx, t.Runtime, GetSession(ctx).InstanceID())
125126
if err != nil {
126127
return "", err
127128
}
128129

129130
// Prepare template data.
130131
session := GetSession(ctx)
131132
data := map[string]any{
132-
"prompt": args.Prompt,
133-
"init_project": args.InitProject,
134-
"current_file_path": args.CurrentFilePath,
135-
"ai_instructions": session.ProjectInstructions(),
136-
"default_olap_connector": olapConnector,
137-
"default_olap_driver": olapDriver,
138-
"default_olap_readwrite": olapModeReadwrite,
133+
"prompt": args.Prompt,
134+
"init_project": args.InitProject,
135+
"current_file_path": args.CurrentFilePath,
136+
"ai_instructions": session.ProjectInstructions(),
137+
"default_olap_info": olapInfo,
139138
}
140139

141140
// Generate the system prompt
@@ -157,9 +156,8 @@ Feel free to change the default OLAP connector if it makes sense for the task.
157156
The user has configured global additional instructions for you. They may not relate to the current request, and may not even relate to your work as a data engineer agent. Only use them if you find them relevant. They are: {{ .ai_instructions }}
158157
{{ end }}
159158
160-
This may not relate to the user's task, but for context, the project's default OLAP connector is named {{ .default_olap_connector }} (driver: {{ .default_olap_driver }}).
161-
{{ if .default_olap_readwrite }} The default OLAP is in readwrite mode, so you can use it in models if you want.
162-
{{ else }} The default OLAP is in read-only mode, so you cannot create models in it. {{ end }}
159+
For context, here are some details about the project's default OLAP connector: {{ .default_olap_info }}.
160+
Note that you can only use it in model resources if it is not readonly.
163161
164162
Call "navigate" tool for the main file created/edited in the conversation. Use kind "file" and pass the written file path.
165163
Prefer dashboard or metrics view files over other files.
@@ -192,25 +190,37 @@ func checkDeveloperAccess(ctx context.Context, rt *runtime.Runtime, internal boo
192190
return ff["developer_agent"], nil
193191
}
194192

195-
// defaultOLAPInfo retrieves the default OLAP connector name, driver, and whether it is in readwrite mode.
196-
func defaultOLAPInfo(ctx context.Context, rt *runtime.Runtime, instanceID string) (string, string, bool, error) {
193+
// defaultOLAPInfo returns info about the default OLAP connector name, driver, and whether it is in readwrite mode.
194+
func defaultOLAPInfo(ctx context.Context, rt *runtime.Runtime, instanceID string) (string, error) {
197195
// Get instance config
198196
inst, err := rt.Instance(ctx, instanceID)
199197
if err != nil {
200-
return "", "", false, err
198+
return "", err
201199
}
202200
olapConnector := inst.ResolveOLAPConnector()
203201

204202
// Open OLAP handle
205-
olap, release, err := rt.AcquireHandle(ctx, instanceID, olapConnector)
203+
handle, release, err := rt.AcquireHandle(ctx, instanceID, olapConnector)
206204
if err != nil {
207-
return "", "", false, err
205+
if errors.Is(err, ctx.Err()) {
206+
return "", err
207+
}
208+
return fmt.Sprintf("name: %q, error: %v", olapConnector, err), nil
208209
}
209210
defer release()
210211

212+
// Get dialect
213+
214+
// Add OLAP dialect
215+
olap, ok := handle.AsOLAP(instanceID)
216+
if !ok {
217+
return fmt.Sprintf("name: %q, error: not an OLAP connector", olapConnector), nil
218+
}
219+
dialect := olap.Dialect()
220+
211221
// Determine if OLAP can run models
212-
_, err = olap.AsModelManager(instanceID)
222+
_, err = handle.AsModelManager(instanceID)
213223
olapModeReadwrite := err == nil
214224

215-
return olapConnector, olap.Driver(), olapModeReadwrite, nil
225+
return fmt.Sprintf("name: %q, driver: %q, dialect: %q, readonly: %v", olapConnector, handle.Driver(), dialect.String(), !olapModeReadwrite), nil
216226
}

runtime/ai/developer_agent_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,38 @@ measures:
253253
// Check that it doesn't have any parse or reconcile errors.
254254
testruntime.RequireReconcileState(t, rt, instanceID, 5, 0, 0)
255255
}
256+
257+
func TestInvalidDefaultOLAP(t *testing.T) {
258+
// Setup a project with a misconfigured default OLAP
259+
rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{
260+
AIConnector: "openai",
261+
Files: map[string]string{
262+
"rill.yaml": `olap_connector: duckdb_missing`,
263+
"connectors/duckdb.yaml": `
264+
type: connector
265+
driver: duckdb
266+
managed: true
267+
`,
268+
},
269+
})
270+
testruntime.RequireReconcileState(t, rt, instanceID, 2, 0, 0)
271+
272+
// Initialize eval
273+
274+
// Ask it to fix the default OLAP connector
275+
s := newEval(t, rt, instanceID)
276+
var res *ai.RouterAgentResult
277+
_, err := s.CallTool(t.Context(), ai.RoleUser, ai.RouterAgentName, &res, ai.RouterAgentArgs{
278+
Prompt: "The default OLAP connector is misconfigured. Can you fix it?",
279+
})
280+
require.NoError(t, err)
281+
require.Equal(t, ai.DeveloperAgentName, res.Agent)
282+
283+
// Check that it doesn't have any parse or reconcile errors.
284+
testruntime.RequireReconcileState(t, rt, instanceID, 2, 0, 0)
285+
286+
// Check it fixed the default OLAP
287+
inst, err := rt.Instance(t.Context(), instanceID)
288+
require.NoError(t, err)
289+
require.Equal(t, "duckdb", inst.ResolveOLAPConnector())
290+
}

0 commit comments

Comments
 (0)