@youdotcom-oss/api 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +36 -36
  2. package/bin/cli.js +233 -400
  3. package/package.json +1 -1
  4. package/src/cli.ts +92 -21
  5. package/src/contents/contents.schemas.ts +8 -7
  6. package/src/contents/tests/contents.request.spec.ts +109 -0
  7. package/src/contents/tests/contents.schema-validation.spec.ts +75 -0
  8. package/src/deep-search/deep-search.schemas.ts +48 -0
  9. package/src/deep-search/deep-search.utils.ts +79 -0
  10. package/src/deep-search/tests/deep-search.request.spec.ts +109 -0
  11. package/src/deep-search/tests/deep-search.schema-validation.spec.ts +71 -0
  12. package/src/deep-search/tests/deep-search.utils.docker.ts +139 -0
  13. package/src/main.ts +4 -3
  14. package/src/search/search.schemas.ts +65 -6
  15. package/src/search/search.utils.ts +6 -28
  16. package/src/search/tests/search.request.spec.ts +122 -0
  17. package/src/search/tests/search.schema-validation.spec.ts +152 -0
  18. package/src/search/tests/{search.utils.spec.ts → search.utils.docker.ts} +0 -10
  19. package/src/shared/api.constants.ts +1 -1
  20. package/src/shared/check-response-for-errors.ts +1 -1
  21. package/src/shared/command-runner.ts +95 -0
  22. package/src/shared/dry-run-utils.ts +141 -0
  23. package/src/shared/tests/command-runner.spec.ts +210 -0
  24. package/src/shared/use-get-user-agents.ts +1 -1
  25. package/src/commands/contents.ts +0 -52
  26. package/src/commands/express.ts +0 -52
  27. package/src/commands/search.ts +0 -52
  28. package/src/express/express.schemas.ts +0 -85
  29. package/src/express/express.utils.ts +0 -113
  30. package/src/express/tests/express.utils.spec.ts +0 -83
  31. /package/src/contents/tests/{contents.utils.spec.ts → contents.utils.docker.ts} +0 -0
@@ -1,113 +0,0 @@
1
- import { EXPRESS_API_URL } from '../shared/api.constants.ts'
2
- import type { GetUserAgent } from '../shared/api.types.ts'
3
- import { checkResponseForErrors } from '../shared/check-response-for-errors.ts'
4
- import {
5
- type ExpressAgentApiResponse,
6
- ExpressAgentApiResponseSchema,
7
- type ExpressAgentInput,
8
- type ExpressAgentMcpResponse,
9
- } from './express.schemas.ts'
10
-
11
- /**
12
- * Checks response status and throws appropriate errors for agent API calls
13
- */
14
- const agentThrowOnFailedStatus = async (response: Response) => {
15
- const errorCode = response.status
16
-
17
- const errorData = (await response.json()) as {
18
- errors?: Array<{ detail?: string }>
19
- }
20
-
21
- if (errorCode === 400) {
22
- throw new Error(`Bad Request:\n${JSON.stringify(errorData)}`)
23
- } else if (errorCode === 401) {
24
- throw new Error(
25
- `Unauthorized: The Agent APIs require a valid You.com API key with agent access. Ensure your YDC_API_KEY has permissions for agent endpoints.`,
26
- )
27
- } else if (errorCode === 403) {
28
- throw new Error(`Forbidden: You are not allowed to use the requested tool for this agent or tenant`)
29
- } else if (errorCode === 429) {
30
- throw new Error('Rate limited by You.com API. Please try again later.')
31
- }
32
- throw new Error(`Failed to call agent. Error code: ${errorCode}`)
33
- }
34
-
35
- export const callExpressAgent = async ({
36
- YDC_API_KEY = process.env.YDC_API_KEY,
37
- agentInput: { input, tools },
38
- getUserAgent,
39
- }: {
40
- agentInput: ExpressAgentInput
41
- YDC_API_KEY?: string
42
- getUserAgent: GetUserAgent
43
- }) => {
44
- const requestBody: {
45
- agent: string
46
- input: string
47
- stream: boolean
48
- tools?: Array<{ type: 'web_search' }>
49
- } = {
50
- agent: 'express',
51
- input,
52
- stream: false, // Use non-streaming JSON response
53
- }
54
-
55
- // Only include tools if provided
56
- if (tools) {
57
- requestBody.tools = tools
58
- }
59
-
60
- const options = {
61
- method: 'POST',
62
- headers: new Headers({
63
- Authorization: `Bearer ${YDC_API_KEY || ''}`,
64
- 'Content-Type': 'application/json',
65
- Accept: 'application/json',
66
- 'User-Agent': getUserAgent(),
67
- }),
68
- body: JSON.stringify(requestBody),
69
- }
70
-
71
- const response = await fetch(EXPRESS_API_URL, options)
72
-
73
- if (!response.ok) {
74
- await agentThrowOnFailedStatus(response)
75
- }
76
-
77
- // Parse JSON response directly
78
- const jsonResponse = await response.json()
79
-
80
- // Check for error field in response
81
- checkResponseForErrors(jsonResponse)
82
-
83
- // Validate API response schema (full response with all fields)
84
- const apiResponse: ExpressAgentApiResponse = ExpressAgentApiResponseSchema.parse(jsonResponse)
85
-
86
- // Find the answer (always present as message.answer, validated by Zod)
87
- const answerItem = apiResponse.output.find((item) => item.type === 'message.answer')
88
- if (!answerItem) {
89
- throw new Error('Express API response missing required message.answer item')
90
- }
91
-
92
- // Find search results (optional, present when web_search tool is used)
93
- const searchItem = apiResponse.output.find((item) => item.type === 'web_search.results')
94
-
95
- // Transform API response to MCP output format (answer + optional search results, token efficient)
96
- const mcpResponse: ExpressAgentMcpResponse = {
97
- answer: answerItem.text,
98
- agent: apiResponse.agent,
99
- }
100
-
101
- // Transform search results if present
102
- if (searchItem && 'content' in searchItem && Array.isArray(searchItem.content)) {
103
- mcpResponse.results = {
104
- web: searchItem.content.map((item) => ({
105
- url: item.url || item.citation_uri || '',
106
- title: item.title || '',
107
- snippet: item.snippet || '',
108
- })),
109
- }
110
- }
111
-
112
- return mcpResponse
113
- }
@@ -1,83 +0,0 @@
1
- import { describe, expect, setDefaultTimeout, test } from 'bun:test'
2
- import { callExpressAgent } from '../express.utils.ts'
3
-
4
- const getUserAgent = () => 'API/test (You.com;TEST)'
5
-
6
- setDefaultTimeout(20_000)
7
-
8
- describe('callExpressAgent', () => {
9
- test(
10
- 'returns answer only (WITHOUT web_search tools)',
11
- async () => {
12
- const result = await callExpressAgent({
13
- agentInput: { input: 'What is machine learning?' },
14
- getUserAgent,
15
- })
16
-
17
- // Verify MCP response structure
18
- expect(result).toHaveProperty('answer')
19
- expect(typeof result.answer).toBe('string')
20
- expect(result.answer.length).toBeGreaterThan(0)
21
-
22
- // Should NOT have results when web_search is not used
23
- expect(result.results).toBeUndefined()
24
-
25
- expect(result.agent).toBe('express')
26
- },
27
- { retry: 2 },
28
- )
29
-
30
- test(
31
- 'returns answer and search results (WITH web_search tools)',
32
- async () => {
33
- const result = await callExpressAgent({
34
- agentInput: {
35
- input: 'Latest developments in quantum computing',
36
- tools: [{ type: 'web_search' }],
37
- },
38
- getUserAgent,
39
- })
40
-
41
- // Verify MCP response has both answer and results
42
- expect(result).toHaveProperty('answer')
43
- expect(typeof result.answer).toBe('string')
44
- expect(result.answer.length).toBeGreaterThan(0)
45
-
46
- expect(result).toHaveProperty('results')
47
- expect(result.results).toHaveProperty('web')
48
- expect(Array.isArray(result.results?.web)).toBe(true)
49
- expect(result.results?.web.length).toBeGreaterThan(0)
50
-
51
- // Verify each search result has required fields
52
- const firstResult = result.results?.web[0]
53
- expect(firstResult).toHaveProperty('url')
54
- expect(firstResult).toHaveProperty('title')
55
- expect(firstResult).toHaveProperty('snippet')
56
- expect(typeof firstResult?.url).toBe('string')
57
- expect(typeof firstResult?.title).toBe('string')
58
- expect(typeof firstResult?.snippet).toBe('string')
59
- expect(firstResult?.url.length).toBeGreaterThan(0)
60
- expect(firstResult?.title.length).toBeGreaterThan(0)
61
-
62
- expect(result.agent).toBe('express')
63
- },
64
- { timeout: 30_000, retry: 2 },
65
- )
66
-
67
- test(
68
- 'works without optional parameters',
69
- async () => {
70
- const result = await callExpressAgent({
71
- agentInput: { input: 'What is the capital of France?' },
72
- getUserAgent,
73
- // No progressToken or sendProgress provided
74
- })
75
-
76
- // Should work normally without progress tracking
77
- expect(result).toHaveProperty('answer')
78
- expect(result.answer.length).toBeGreaterThan(0)
79
- expect(result.agent).toBe('express')
80
- },
81
- { retry: 2 },
82
- )
83
- })