You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
4.0 KiB
Go

/*
* Copyright 2026 CloudWeGo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// smoketest is a standalone CLI that exercises the full pipeline without the browser:
//
// go run ./smoketest "what can you do?"
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
localbk "github.com/cloudwego/eino-ext/adk/backend/local"
"github.com/cloudwego/eino/adk"
"github.com/cloudwego/eino/adk/prebuilt/deep"
"github.com/cloudwego/eino/schema"
"github.com/cloudwego/eino-examples/adk/common/model"
"github.com/cloudwego/eino-examples/quickstart/chatwithdoc/a2ui"
)
func main() {
query := "what can you do?"
if len(os.Args) > 1 {
query = os.Args[1]
}
ctx := context.Background()
agent, err := func() (adk.Agent, error) {
backend, err := localbk.NewBackend(ctx, &localbk.Config{})
if err != nil {
return nil, err
}
return deep.New(ctx, &deep.Config{
Name: "ChatWithDocAgent",
Description: "An agent that reads and answers questions about documents.",
ChatModel: model.NewChatModel(),
Backend: backend,
StreamingShell: backend,
MaxIteration: 10,
})
}()
if err != nil {
fmt.Fprintf(os.Stderr, "buildAgent: %v\n", err)
os.Exit(1)
}
runner := adk.NewRunner(ctx, adk.RunnerConfig{
Agent: agent,
EnableStreaming: true,
})
messages := []*schema.Message{schema.UserMessage(query)}
fmt.Printf("→ user: %s\n\n", query)
iter := runner.Run(ctx, messages)
lastContent, interruptID, _, err := a2ui.StreamToWriter(&jsonlPrinter{}, "smoketest", messages, iter)
fmt.Println()
if err != nil {
fmt.Fprintf(os.Stderr, "stream error: %v\n", err)
os.Exit(1)
}
if interruptID != "" {
fmt.Printf("\n⏸ agent interrupted (id=%s); re-run with approval to continue\n", interruptID)
return
}
fmt.Printf("\n← final content (%d chars):\n%s\n", len(lastContent), lastContent)
}
// jsonlPrinter writes each A2UI JSONL line to stdout with a human-readable summary.
type jsonlPrinter struct {
buf []byte
}
func (p *jsonlPrinter) Write(b []byte) (int, error) {
p.buf = append(p.buf, b...)
for {
idx := -1
for i, c := range p.buf {
if c == '\n' {
idx = i
break
}
}
if idx < 0 {
break
}
line := p.buf[:idx]
p.buf = p.buf[idx+1:]
if len(line) == 0 {
continue
}
p.printLine(line)
}
return len(b), nil
}
func (p *jsonlPrinter) printLine(line []byte) {
var msg a2ui.Message
if err := json.Unmarshal(line, &msg); err != nil {
fmt.Printf("[raw] %s\n", line)
return
}
switch {
case msg.BeginRendering != nil:
fmt.Printf("[beginRendering] surface=%s root=%s\n",
msg.BeginRendering.SurfaceID, msg.BeginRendering.Root)
case msg.SurfaceUpdate != nil:
for _, c := range msg.SurfaceUpdate.Components {
switch {
case c.Component.Text != nil && c.Component.Text.Value != "":
fmt.Printf("[surfaceUpdate] %s: Text=%q\n", c.ID, truncate(c.Component.Text.Value, 60))
case c.Component.Column != nil:
fmt.Printf("[surfaceUpdate] %s: Column children=%v\n", c.ID, c.Component.Column.Children)
case c.Component.Card != nil:
fmt.Printf("[surfaceUpdate] %s: Card\n", c.ID)
}
}
case msg.DataModelUpdate != nil:
for _, dc := range msg.DataModelUpdate.Contents {
fmt.Printf("[dataModelUpdate] %s = %q\n", dc.Key, truncate(dc.ValueString, 80))
}
}
}
func truncate(s string, n int) string {
r := []rune(s)
if len(r) <= n {
return s
}
return string(r[:n]) + "…"
}
// Ensure io.EOF is imported (used by a2ui internally).
var _ = errors.Is(io.EOF, io.EOF)