Skip to content

Commit 8f226f8

Browse files
author
stlc-bot
committed
feat(answers): add live web research with fast and ultra modes (#954)
Stainless-Generated-From: 462dacbcb30fa1a30926f16d800581d9e4cb508b
1 parent 97370fd commit 8f226f8

4 files changed

Lines changed: 115 additions & 1 deletion

File tree

‎.stats.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
configured_endpoints: 47
1+
configured_endpoints: 48

‎pkg/cmd/cmd.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ func init() {
9797
Category: "API RESOURCE",
9898
Suggest: true,
9999
Commands: []*cli.Command{
100+
&webAnswers,
100101
&webExtract,
101102
&webExtractCompetitors,
102103
&webExtractFonts,

‎pkg/cmd/web.go‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,42 @@ import (
1414
"github.com/urfave/cli/v3"
1515
)
1616

17+
var webAnswers = cli.Command{
18+
Name: "answers",
19+
Usage: "Researches the live web and returns a sourced answer in your requested JSON\nshape. Select fast for a smaller research budget at 10 credits or ultra for\ndeeper reasoning at 100 credits. Defaults to ultra. Fast research is limited to\n30 seconds and ultra to 50 seconds; timeoutMS can shorten either deadline.",
20+
Suggest: true,
21+
Flags: []cli.Flag{
22+
&requestflag.Flag[string]{
23+
Name: "task",
24+
Usage: `What to research and answer, in plain language. Naming a domain in the task (for example "pricing on context.dev") makes the agent read that site before it searches.`,
25+
Required: true,
26+
BodyPath: "task",
27+
},
28+
&requestflag.Flag[map[string]any]{
29+
Name: "json-format",
30+
Usage: `An example object with placeholder values (for example {"pricing_page_url": "", "plans": [{"name": "", "price": 0}]}). Object keys and value types are preserved; unknown values may be null. Empty arrays accept any JSON items. Defaults to {"result": ""}. Maximum 8 levels, 500 values, and 16000 characters.`,
31+
BodyPath: "json_format",
32+
},
33+
&requestflag.Flag[string]{
34+
Name: "mode",
35+
Usage: "Research level: fast uses a smaller model and research budget for 10 credits; ultra uses deeper reasoning and research for 100 credits. Defaults to ultra. Only successful requests consume credits.",
36+
BodyPath: "mode",
37+
},
38+
&requestflag.Flag[[]string]{
39+
Name: "tag",
40+
Usage: "Optional tags for tracking usage. Up to 20 tags, each 1 to 50 characters.",
41+
BodyPath: "tags",
42+
},
43+
&requestflag.Flag[int64]{
44+
Name: "timeout-ms",
45+
Usage: "Optional timeout in milliseconds for the request. If the request takes longer than this value, it will be aborted with a 408 status code. Maximum allowed value is 300000ms (5 minutes).",
46+
BodyPath: "timeoutMS",
47+
},
48+
},
49+
Action: handleWebAnswers,
50+
HideHelpCommand: true,
51+
}
52+
1753
var webExtract = requestflag.WithInnerFlags(cli.Command{
1854
Name: "extract",
1955
Usage: "Crawl a website, use the provided JSON Schema and instructions to prioritize\nrelevant internal links, and extract structured data from the selected pages.",
@@ -1010,6 +1046,47 @@ var webWebScrapeSitemap = cli.Command{
10101046
HideHelpCommand: true,
10111047
}
10121048

1049+
func handleWebAnswers(ctx context.Context, cmd *cli.Command) error {
1050+
client := contextdev.NewClient(getDefaultRequestOptions(cmd)...)
1051+
unusedArgs := cmd.Args().Slice()
1052+
1053+
if len(unusedArgs) > 0 {
1054+
return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs)
1055+
}
1056+
1057+
options, err := flagOptions(
1058+
cmd,
1059+
apiquery.NestedQueryFormatBrackets,
1060+
apiquery.ArrayQueryFormatComma,
1061+
ApplicationJSON,
1062+
false,
1063+
)
1064+
if err != nil {
1065+
return err
1066+
}
1067+
1068+
params := contextdev.WebAnswersParams{}
1069+
1070+
var res []byte
1071+
options = append(options, option.WithResponseBodyInto(&res))
1072+
_, err = client.Web.Answers(ctx, params, options...)
1073+
if err != nil {
1074+
return err
1075+
}
1076+
1077+
obj := gjson.ParseBytes(res)
1078+
format := cmd.Root().String("format")
1079+
explicitFormat := cmd.Root().IsSet("format")
1080+
transform := cmd.Root().String("transform")
1081+
return ShowJSON(obj, ShowJSONOpts{
1082+
ExplicitFormat: explicitFormat,
1083+
Format: format,
1084+
RawOutput: cmd.Root().Bool("raw-output"),
1085+
Title: "web answers",
1086+
Transform: transform,
1087+
})
1088+
}
1089+
10131090
func handleWebExtract(ctx context.Context, cmd *cli.Command) error {
10141091
client := contextdev.NewClient(getDefaultRequestOptions(cmd)...)
10151092
unusedArgs := cmd.Args().Slice()

‎pkg/cmd/web_test.go‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,42 @@ import (
99
"github.com/context-dot-dev/context-dev-cli/internal/requestflag"
1010
)
1111

12+
func TestWebAnswers(t *testing.T) {
13+
t.Skip("Mock server tests are disabled")
14+
t.Run("regular flags", func(t *testing.T) {
15+
mocktest.TestRunMockTestWithFlags(
16+
t,
17+
"--api-key", "string",
18+
"web", "answers",
19+
"--task", "Find the pricing page URL and plan names for context.dev.",
20+
"--json-format", "{pricing_page_url: bar, plans: bar}",
21+
"--mode", "fast",
22+
"--tag", "production",
23+
"--tag", "team-alpha",
24+
"--timeout-ms", "1000",
25+
)
26+
})
27+
28+
t.Run("piping data", func(t *testing.T) {
29+
// Test piping YAML data over stdin
30+
pipeData := []byte("" +
31+
"task: Find the pricing page URL and plan names for context.dev.\n" +
32+
"json_format:\n" +
33+
" pricing_page_url: bar\n" +
34+
" plans: bar\n" +
35+
"mode: fast\n" +
36+
"tags:\n" +
37+
" - production\n" +
38+
" - team-alpha\n" +
39+
"timeoutMS: 1000\n")
40+
mocktest.TestRunMockTestWithPipeAndFlags(
41+
t, pipeData,
42+
"--api-key", "string",
43+
"web", "answers",
44+
)
45+
})
46+
}
47+
1248
func TestWebExtract(t *testing.T) {
1349
t.Skip("Mock server tests are disabled")
1450
t.Run("regular flags", func(t *testing.T) {

0 commit comments

Comments
 (0)