Skip to content

Commit b7619fe

Browse files
author
Craig D Wilhite
authored
Merge pull request #298 from faroshq/codex/app-studio-eino-01-deps-contracts
Add App Studio assistant engine contracts
2 parents 8e06eb4 + 694ffe5 commit b7619fe

4 files changed

Lines changed: 330 additions & 0 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
Copyright 2026 The Faros Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package api
18+
19+
import (
20+
"context"
21+
"encoding/json"
22+
"time"
23+
24+
aiv1alpha1 "github.com/faroshq/provider-app-studio/apis/ai/v1alpha1"
25+
asclient "github.com/faroshq/provider-app-studio/client"
26+
"github.com/faroshq/provider-app-studio/store"
27+
"github.com/faroshq/provider-app-studio/workspace"
28+
)
29+
30+
// projectAssistantEngine is App Studio's private boundary around assistant
31+
// execution. Eino implementations plug in behind this contract; REST payloads,
32+
// project APIs, and portal state stay App Studio-owned.
33+
type projectAssistantEngine interface {
34+
StreamProjectAssistant(
35+
context.Context,
36+
projectAssistantRunRequest,
37+
projectAssistantEventSink,
38+
) (projectAssistantRunResult, error)
39+
}
40+
41+
type projectAssistantRunRequest struct {
42+
Identity identity
43+
Client *asclient.Client
44+
Project *aiv1alpha1.Project
45+
Repository *ProjectRepositoryView
46+
WorkspaceScope workspace.Scope
47+
Workspace *workspace.FileStore
48+
MessageScope store.Scope
49+
LLM projectLLMSettings
50+
History []store.Message
51+
MCPBaseURL string
52+
MCPInsecureSkipTLSVerify bool
53+
}
54+
55+
type projectAssistantRunResult struct {
56+
Content string
57+
Events []projectAssistantEvent
58+
ToolCalls []projectAssistantToolCall
59+
}
60+
61+
type projectAssistantEventSink interface {
62+
EmitProjectAssistantEvent(context.Context, projectAssistantEvent) error
63+
}
64+
65+
type projectAssistantEvent struct {
66+
Type projectAssistantEventType `json:"type"`
67+
ID string `json:"id,omitempty"`
68+
MessageID string `json:"messageID,omitempty"`
69+
ToolCall *projectAssistantToolCall `json:"toolCall,omitempty"`
70+
Permission *projectAssistantPermission `json:"permission,omitempty"`
71+
Checkpoint *projectAssistantCheckpoint `json:"checkpoint,omitempty"`
72+
Delta string `json:"delta,omitempty"`
73+
Status string `json:"status,omitempty"`
74+
Error string `json:"error,omitempty"`
75+
Metadata json.RawMessage `json:"metadata,omitempty"`
76+
CreatedAt *time.Time `json:"createdAt,omitempty"`
77+
}
78+
79+
type projectAssistantEventType string
80+
81+
const (
82+
projectAssistantEventRunStarted projectAssistantEventType = "run_started"
83+
projectAssistantEventMessageDelta projectAssistantEventType = "message_delta"
84+
projectAssistantEventStatus projectAssistantEventType = "status"
85+
projectAssistantEventToolCallStarted projectAssistantEventType = "tool_call_started"
86+
projectAssistantEventToolCallFinished projectAssistantEventType = "tool_call_finished"
87+
projectAssistantEventPermissionNeeded projectAssistantEventType = "permission_required"
88+
projectAssistantEventCheckpointSaved projectAssistantEventType = "checkpoint_saved"
89+
projectAssistantEventRunFailed projectAssistantEventType = "run_failed"
90+
projectAssistantEventRunFinished projectAssistantEventType = "run_finished"
91+
)
92+
93+
type projectAssistantToolCall struct {
94+
ID string `json:"id"`
95+
Name string `json:"name"`
96+
Status string `json:"status,omitempty"`
97+
Summary string `json:"summary,omitempty"`
98+
Input json.RawMessage `json:"input,omitempty"`
99+
Result json.RawMessage `json:"result,omitempty"`
100+
}
101+
102+
type projectAssistantPermission struct {
103+
ID string `json:"id"`
104+
ToolCallID string `json:"toolCallID,omitempty"`
105+
ToolName string `json:"toolName,omitempty"`
106+
Reason string `json:"reason,omitempty"`
107+
Input json.RawMessage `json:"input,omitempty"`
108+
}
109+
110+
type projectAssistantCheckpoint struct {
111+
ID string `json:"id"`
112+
Reason string `json:"reason,omitempty"`
113+
CreatedAt *time.Time `json:"createdAt,omitempty"`
114+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright 2026 The Faros Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package api
18+
19+
import (
20+
"context"
21+
"encoding/json"
22+
"strings"
23+
"testing"
24+
25+
"github.com/cloudwego/eino/adk"
26+
)
27+
28+
func TestProjectAssistantContractCanReferenceEinoADK(t *testing.T) {
29+
runner := adk.NewRunner(context.Background(), adk.RunnerConfig{
30+
EnableStreaming: true,
31+
})
32+
if runner == nil {
33+
t.Fatal("adk.NewRunner returned nil")
34+
}
35+
36+
input := adk.AgentInput{EnableStreaming: true}
37+
if !input.EnableStreaming {
38+
t.Fatal("adk.AgentInput did not preserve streaming mode")
39+
}
40+
}
41+
42+
func TestProjectAssistantEventSinkContract(t *testing.T) {
43+
sink := projectAssistantEventSink(projectAssistantEventSinkFunc(func(context.Context, projectAssistantEvent) error {
44+
return nil
45+
}))
46+
if err := sink.EmitProjectAssistantEvent(context.Background(), projectAssistantEvent{
47+
Type: projectAssistantEventRunStarted,
48+
}); err != nil {
49+
t.Fatalf("EmitProjectAssistantEvent returned error: %v", err)
50+
}
51+
}
52+
53+
func TestProjectAssistantEventOmitsEmptyOptionalTimestamps(t *testing.T) {
54+
payload, err := json.Marshal(projectAssistantEvent{
55+
Type: projectAssistantEventCheckpointSaved,
56+
Checkpoint: &projectAssistantCheckpoint{
57+
ID: "checkpoint-1",
58+
},
59+
})
60+
if err != nil {
61+
t.Fatalf("Marshal returned error: %v", err)
62+
}
63+
if strings.Contains(string(payload), "createdAt") {
64+
t.Fatalf("event encoded empty createdAt: %s", payload)
65+
}
66+
}
67+
68+
type projectAssistantEventSinkFunc func(context.Context, projectAssistantEvent) error
69+
70+
func (f projectAssistantEventSinkFunc) EmitProjectAssistantEvent(
71+
ctx context.Context,
72+
event projectAssistantEvent,
73+
) error {
74+
return f(ctx, event)
75+
}

providers/app-studio/go.mod

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/faroshq/provider-app-studio
33
go 1.26.3
44

55
require (
6+
github.com/cloudwego/eino v0.9.8
67
github.com/faroshq/provider-sdk v0.0.1
78
github.com/google/uuid v1.6.0
89
github.com/gorilla/mux v1.8.1
@@ -16,22 +17,44 @@ require (
1617

1718
require (
1819
cloud.google.com/go/compute/metadata v0.3.0 // indirect
20+
github.com/bahlo/generic-list-go v0.2.0 // indirect
21+
github.com/buger/jsonparser v1.1.1 // indirect
22+
github.com/bytedance/gopkg v0.1.3 // indirect
23+
github.com/bytedance/sonic v1.15.0 // indirect
24+
github.com/bytedance/sonic/loader v0.5.0 // indirect
25+
github.com/cloudwego/base64x v0.1.6 // indirect
1926
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
27+
github.com/dustin/go-humanize v1.0.1 // indirect
28+
github.com/eino-contrib/jsonschema v1.0.3 // indirect
2029
github.com/fxamacker/cbor/v2 v2.9.1 // indirect
2130
github.com/go-logr/logr v1.4.3 // indirect
31+
github.com/goph/emperror v0.17.2 // indirect
2232
github.com/json-iterator/go v1.1.12 // indirect
33+
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
34+
github.com/mailru/easyjson v0.7.7 // indirect
2335
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
2436
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
2537
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
38+
github.com/nikolalohinski/gonja v1.5.3 // indirect
39+
github.com/pelletier/go-toml/v2 v2.0.9 // indirect
40+
github.com/pkg/errors v0.9.1 // indirect
41+
github.com/sirupsen/logrus v1.9.3 // indirect
42+
github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f // indirect
2643
github.com/spf13/pflag v1.0.10 // indirect
44+
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
45+
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
2746
github.com/x448/float16 v0.8.4 // indirect
47+
github.com/yargevad/filepathx v1.0.0 // indirect
2848
go.yaml.in/yaml/v2 v2.4.4 // indirect
49+
golang.org/x/arch v0.11.0 // indirect
50+
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect
2951
golang.org/x/net v0.53.0 // indirect
3052
golang.org/x/sys v0.43.0 // indirect
3153
golang.org/x/term v0.42.0 // indirect
3254
golang.org/x/text v0.36.0 // indirect
3355
golang.org/x/time v0.15.0 // indirect
3456
gopkg.in/inf.v0 v0.9.1 // indirect
57+
gopkg.in/yaml.v3 v3.0.1 // indirect
3558
k8s.io/kube-openapi v0.0.0-20260414162039-ec9c827d403f // indirect
3659
k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 // indirect
3760
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect

0 commit comments

Comments
 (0)