Files
ragflow/internal/agent/component/tavily_component_test.go
Hz_ b29a4a61eb fix(ao-agent): add Tavily input_form, wire real search component, and port TavilyExtract to Go (#16693)
### Summary

- TavilySearch now stores api_key from component params and injects it
into tool calls when runtime inputs omit it.
- TavilyExtract and BGPT now follow the same stored api_key behavior.
- Canvas decoding now recovers api_key from graph.nodes[].data.form when
components[].obj.params.api_key is empty, matching frontend payload
behavior without changing frontend data.
- Added regression tests for graph form key recovery and stored key
injection / caller key precedence.

Tests: build.sh --test ./internal/agent/component ./internal/service —
all pass.

<img width="1476" height="850" alt="image"
src="https://github.com/user-attachments/assets/0be31587-c1ba-4f3e-b43a-4fe0fca5a44c"
/>

<img width="1476" height="850" alt="image"
src="https://github.com/user-attachments/assets/e3edd92c-c62e-4db4-abe2-772bdf4fe1b2"
/>
2026-07-09 16:38:42 +08:00

103 lines
3.1 KiB
Go

package component
import (
"context"
"testing"
)
func TestTavilySearch_RegisteredRealComponentWithInputForm(t *testing.T) {
c, err := New("TavilySearch", nil)
if err != nil {
t.Fatalf("New(TavilySearch): %v", err)
}
if _, ok := c.(*tavilySearchComponent); !ok {
t.Fatalf("New(TavilySearch) returned %T, want *tavilySearchComponent", c)
}
formGetter, ok := c.(interface{ GetInputForm() map[string]any })
if !ok {
t.Fatal("TavilySearch component does not expose GetInputForm")
}
form := formGetter.GetInputForm()
query, ok := form["query"].(map[string]any)
if !ok {
t.Fatalf("GetInputForm()[query] has type %T, want map", form["query"])
}
if query["type"] != "line" || query["name"] != "Query" {
t.Fatalf("GetInputForm()[query] = %#v, want Query line input", query)
}
}
func TestTavilyExtract_RegisteredWithInputForm(t *testing.T) {
c, err := New("TavilyExtract", nil)
if err != nil {
t.Fatalf("New(TavilyExtract): %v", err)
}
if _, ok := c.(*tavilyExtractComponent); !ok {
t.Fatalf("New(TavilyExtract) returned %T, want *tavilyExtractComponent", c)
}
formGetter, ok := c.(interface{ GetInputForm() map[string]any })
if !ok {
t.Fatal("TavilyExtract component does not expose GetInputForm")
}
form := formGetter.GetInputForm()
urls, ok := form["urls"].(map[string]any)
if !ok {
t.Fatalf("GetInputForm()[urls] has type %T, want map", form["urls"])
}
if urls["type"] != "line" || urls["name"] != "URLs" {
t.Fatalf("GetInputForm()[urls] = %#v, want URLs line input", urls)
}
}
func TestTavilySearch_StoresAPIKeyAndInjectsWhenInputOmitsIt(t *testing.T) {
c, err := New("TavilySearch", map[string]any{"api_key": "tvly-stored"})
if err != nil {
t.Fatalf("New(TavilySearch): %v", err)
}
tc, ok := c.(*tavilySearchComponent)
if !ok {
t.Fatalf("New(TavilySearch) returned %T, want *tavilySearchComponent", c)
}
if tc.apiKey != "tvly-stored" {
t.Fatalf("stored apiKey = %q, want tvly-stored", tc.apiKey)
}
inputs := map[string]any{"query": ""}
if _, err := tc.Invoke(context.Background(), inputs); err != nil {
t.Fatalf("Invoke with empty query errored: %v", err)
}
if got := inputs["api_key"]; got != "tvly-stored" {
t.Fatalf("injected api_key = %v, want tvly-stored", got)
}
}
func TestTavilySearch_DoesNotOverrideCallerAPIKey(t *testing.T) {
c, err := New("TavilySearch", map[string]any{"api_key": "tvly-stored"})
if err != nil {
t.Fatalf("New(TavilySearch): %v", err)
}
tc := c.(*tavilySearchComponent)
inputs := map[string]any{"query": "", "api_key": "tvly-call"}
if _, err := tc.Invoke(context.Background(), inputs); err != nil {
t.Fatalf("Invoke with empty query errored: %v", err)
}
if got := inputs["api_key"]; got != "tvly-call" {
t.Fatalf("api_key = %v, want caller key", got)
}
}
func TestTavilyExtract_StoresAPIKey(t *testing.T) {
c, err := New("TavilyExtract", map[string]any{"api_key": "tvly-extract"})
if err != nil {
t.Fatalf("New(TavilyExtract): %v", err)
}
tc, ok := c.(*tavilyExtractComponent)
if !ok {
t.Fatalf("New(TavilyExtract) returned %T, want *tavilyExtractComponent", c)
}
if tc.apiKey != "tvly-extract" {
t.Fatalf("stored apiKey = %q, want tvly-extract", tc.apiKey)
}
}