kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: add examples to toolconfigs and example-usage
rodzic
59bda8c984
commit
c7aa791a9f
|
@ -456,6 +456,46 @@
|
|||
"minLength": 1
|
||||
},
|
||||
"description": "Allows you to override this tool's behavior or disable it entirely for different pricing plans. This is a map of PricingPlan slug to PricingPlanToolOverrides for that plan."
|
||||
},
|
||||
"examples": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The display name of the example."
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string",
|
||||
"description": "The input prompt for agents to use when running this example."
|
||||
},
|
||||
"systemPrompt": {
|
||||
"type": "string",
|
||||
"description": "An optional system prompt for agents to use when running this example. Defaults to `You are a helpful assistant. Be as concise as possible.`"
|
||||
},
|
||||
"args": {
|
||||
"type": "object",
|
||||
"additionalProperties": {},
|
||||
"description": "The arguments to pass to the tool for this example."
|
||||
},
|
||||
"featured": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this example should be featured in the docs for the project."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "A description of the example."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"prompt",
|
||||
"args"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"description": "Examples of how to use this tool. Used to generate example usage in the tool's docs."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
|
@ -5,9 +5,10 @@ import { assert, omit, sanitizeSearchParams } from '@agentic/platform-core'
|
|||
import { ExternalLinkIcon } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import { useAgentic } from '@/components/agentic-provider'
|
||||
import { ExampleUsage } from '@/components/example-usage'
|
||||
import { LoadingIndicator } from '@/components/loading-indicator'
|
||||
import { PageContainer } from '@/components/page-container'
|
||||
import { ProjectPricingPlans } from '@/components/project-pricing-plans'
|
||||
|
@ -144,6 +145,24 @@ export function MarketplaceProjectIndex({
|
|||
hasInitializedCheckoutFromSearchParams
|
||||
])
|
||||
|
||||
const featuredToolName = useMemo(() => {
|
||||
const deployment = project?.lastPublishedDeployment
|
||||
const toolConfigs = deployment?.toolConfigs?.filter(
|
||||
(toolConfig) => toolConfig?.enabled !== false
|
||||
)
|
||||
|
||||
return (
|
||||
toolConfigs?.find((toolConfig) =>
|
||||
toolConfig.examples?.find((example) => example.featured)
|
||||
)?.name ??
|
||||
toolConfigs?.find((toolConfig) => toolConfig.examples?.length)?.name ??
|
||||
toolConfigs?.[0]?.name ??
|
||||
deployment?.tools[0]?.name
|
||||
)
|
||||
}, [project])
|
||||
|
||||
const deployment = project?.lastPublishedDeployment
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<section>
|
||||
|
@ -181,6 +200,31 @@ export function MarketplaceProjectIndex({
|
|||
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
|
||||
Overview
|
||||
</h2>
|
||||
|
||||
<div
|
||||
className={`grid grid-cols grid-cols-1 gap-8 md:grid-cols-2`}
|
||||
>
|
||||
<div className='flex flex-col gap-4'>
|
||||
{deployment ? (
|
||||
<p>
|
||||
{deployment?.description ||
|
||||
'No description available'}
|
||||
</p>
|
||||
) : (
|
||||
<p>
|
||||
This project doesn't have any published deployments.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='flex flex-col gap-4'>
|
||||
<ExampleUsage
|
||||
projectIdentifier={projectIdentifier}
|
||||
project={project}
|
||||
tool={featuredToolName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value='tools' className='flex flex-col gap-4'>
|
||||
|
|
|
@ -18,7 +18,7 @@ import { globalAgenticApiClient } from '@/lib/global-api'
|
|||
|
||||
export default async function TheBestDamnLandingPageEver() {
|
||||
const projectIdentifier = '@agentic/search'
|
||||
const prompt = 'What is the latest news about AI?'
|
||||
const tool = 'search'
|
||||
|
||||
const initialProject =
|
||||
await globalAgenticApiClient.getPublicProjectByIdentifier({
|
||||
|
@ -32,7 +32,7 @@ export default async function TheBestDamnLandingPageEver() {
|
|||
project: initialProject,
|
||||
deployment: initialProject.lastPublishedDeployment!,
|
||||
identifier: projectIdentifier,
|
||||
prompt
|
||||
tool
|
||||
})
|
||||
const initialCodeBlock = await highlight(initialCodeSnippet)
|
||||
|
||||
|
@ -61,8 +61,8 @@ export default async function TheBestDamnLandingPageEver() {
|
|||
|
||||
<ExampleUsage
|
||||
projectIdentifier={projectIdentifier}
|
||||
prompt={prompt}
|
||||
project={initialProject}
|
||||
tool={tool}
|
||||
initialCodeBlock={initialCodeBlock}
|
||||
/>
|
||||
|
||||
|
|
|
@ -33,13 +33,13 @@ import { LoadingIndicator } from './loading-indicator'
|
|||
|
||||
export function ExampleUsage({
|
||||
projectIdentifier,
|
||||
prompt,
|
||||
project: initialProject,
|
||||
tool,
|
||||
initialCodeBlock
|
||||
}: {
|
||||
projectIdentifier: string
|
||||
prompt: string
|
||||
project?: Project
|
||||
tool?: string
|
||||
initialCodeBlock?: JSX.Element
|
||||
}) {
|
||||
const ctx = useAgentic()
|
||||
|
@ -94,7 +94,7 @@ export function ExampleUsage({
|
|||
<div className='w-full max-w-3xl flex flex-col items-center border rounded-lg shadow-sm p-2 md:p-4 bg-background'>
|
||||
<ExampleUsageContent
|
||||
projectIdentifier={projectIdentifier}
|
||||
prompt={prompt}
|
||||
tool={tool}
|
||||
initialCodeBlock={initialCodeBlock}
|
||||
isLoading={isLoading}
|
||||
isError={isError}
|
||||
|
@ -108,7 +108,7 @@ export function ExampleUsage({
|
|||
|
||||
function ExampleUsageContent({
|
||||
projectIdentifier,
|
||||
prompt,
|
||||
tool,
|
||||
initialCodeBlock,
|
||||
isLoading,
|
||||
isError,
|
||||
|
@ -117,7 +117,7 @@ function ExampleUsageContent({
|
|||
setConfig
|
||||
}: {
|
||||
projectIdentifier: string
|
||||
prompt: string
|
||||
tool?: string
|
||||
initialCodeBlock?: JSX.Element
|
||||
isLoading: boolean
|
||||
isError: boolean
|
||||
|
@ -146,7 +146,7 @@ function ExampleUsageContent({
|
|||
project,
|
||||
deployment,
|
||||
identifier: projectIdentifier,
|
||||
prompt
|
||||
tool
|
||||
})
|
||||
|
||||
return (
|
||||
|
@ -163,7 +163,12 @@ function ExampleUsageContent({
|
|||
>
|
||||
<TabsList>
|
||||
{targets.map((target) => (
|
||||
<TabsTrigger key={target} value={target} className='cursor-pointer'>
|
||||
<TabsTrigger
|
||||
key={target}
|
||||
value={target}
|
||||
className='cursor-pointer'
|
||||
disabled={target === 'http' && !tool}
|
||||
>
|
||||
{targetLabels[target]}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { Deployment, Project } from '@agentic/platform-types'
|
||||
import type { BundledLanguage } from 'shiki/bundle/web'
|
||||
import type { Simplify } from 'type-fest'
|
||||
import { assert } from '@agentic/platform-core'
|
||||
|
||||
import { gatewayBaseUrl } from './config'
|
||||
|
@ -95,33 +96,68 @@ export type GetCodeForDeveloperConfigOpts = {
|
|||
project: Project
|
||||
deployment: Deployment
|
||||
identifier: string
|
||||
prompt: string
|
||||
tool?: string
|
||||
}
|
||||
|
||||
type GetCodeForDeveloperConfigInnerOpts = Simplify<
|
||||
GetCodeForDeveloperConfigOpts & {
|
||||
systemPrompt: string
|
||||
prompt: string
|
||||
args: Record<string, any>
|
||||
}
|
||||
>
|
||||
|
||||
export function getCodeForDeveloperConfig(
|
||||
opts: GetCodeForDeveloperConfigOpts
|
||||
): CodeSnippet {
|
||||
const { config } = opts
|
||||
const { config, tool } = opts
|
||||
|
||||
const toolConfig = tool
|
||||
? opts.deployment.toolConfigs.find((toolConfig) => toolConfig.name === tool)
|
||||
: undefined
|
||||
const toolConfigExample =
|
||||
toolConfig?.examples?.find((example) => example.featured) ??
|
||||
toolConfig?.examples?.[0]
|
||||
|
||||
const innerOpts: GetCodeForDeveloperConfigInnerOpts = {
|
||||
...opts,
|
||||
|
||||
// TODO: incorporate the system message into all of the example TS and
|
||||
// Python code snippets
|
||||
systemPrompt:
|
||||
toolConfigExample?.systemPrompt ??
|
||||
'You are a helpful assistant. Be as concise as possible.',
|
||||
|
||||
// TODO: generate this default on the backend based on the tool's name,
|
||||
// description, inputSchema, and outputSchema
|
||||
prompt: toolConfigExample?.prompt ?? 'What is the latest news about AI?',
|
||||
|
||||
// TODO: generate this default on the backend based on the tool's input
|
||||
// schema
|
||||
// TODO: if no `args` are provided, hide the `HTTP` tab?
|
||||
args: toolConfigExample?.args ?? {
|
||||
query: 'example search query'
|
||||
}
|
||||
}
|
||||
|
||||
switch (config.target) {
|
||||
case 'mcp':
|
||||
return getCodeForMCPClientConfig(opts)
|
||||
return getCodeForMCPClientConfig(innerOpts)
|
||||
|
||||
case 'typescript':
|
||||
return getCodeForTSFrameworkConfig(opts)
|
||||
return getCodeForTSFrameworkConfig(innerOpts)
|
||||
|
||||
case 'python':
|
||||
return getCodeForPythonFrameworkConfig(opts)
|
||||
return getCodeForPythonFrameworkConfig(innerOpts)
|
||||
|
||||
case 'http':
|
||||
return getCodeForHTTPConfig(opts)
|
||||
return getCodeForHTTPConfig(innerOpts)
|
||||
}
|
||||
}
|
||||
|
||||
export function getCodeForMCPClientConfig({
|
||||
identifier
|
||||
}: GetCodeForDeveloperConfigOpts): CodeSnippet {
|
||||
}: GetCodeForDeveloperConfigInnerOpts): CodeSnippet {
|
||||
const mcpUrl = `${gatewayBaseUrl}/${identifier}/mcp`
|
||||
return {
|
||||
code: mcpUrl,
|
||||
|
@ -132,8 +168,9 @@ export function getCodeForMCPClientConfig({
|
|||
export function getCodeForTSFrameworkConfig({
|
||||
config,
|
||||
identifier,
|
||||
prompt
|
||||
}: GetCodeForDeveloperConfigOpts): CodeSnippet {
|
||||
prompt,
|
||||
systemPrompt
|
||||
}: GetCodeForDeveloperConfigInnerOpts): CodeSnippet {
|
||||
switch (config.tsFrameworkTarget) {
|
||||
case 'ai':
|
||||
return {
|
||||
|
@ -288,7 +325,7 @@ const searchTool = await AgenticToolClient.fromIdentifier('${identifier}')
|
|||
const exampleAgent = new Agent({
|
||||
name: 'Example Agent',
|
||||
model: openai('gpt-4o-mini') as any,
|
||||
instructions: 'You are a helpful assistant. Be as concise as possible.',
|
||||
instructions: '${systemPrompt}',
|
||||
tools: createMastraTools(searchTool)
|
||||
})
|
||||
|
||||
|
@ -357,7 +394,7 @@ export function getCodeForPythonFrameworkConfig({
|
|||
config,
|
||||
identifier,
|
||||
prompt
|
||||
}: GetCodeForDeveloperConfigOpts): CodeSnippet {
|
||||
}: GetCodeForDeveloperConfigInnerOpts): CodeSnippet {
|
||||
const mcpUrl = `${gatewayBaseUrl}/${identifier}/mcp`
|
||||
|
||||
switch (config.pyFrameworkTarget) {
|
||||
|
@ -441,8 +478,9 @@ export function getCodeForHTTPConfig({
|
|||
config,
|
||||
identifier,
|
||||
deployment,
|
||||
tool
|
||||
}: GetCodeForDeveloperConfigOpts): CodeSnippet {
|
||||
tool,
|
||||
args
|
||||
}: GetCodeForDeveloperConfigInnerOpts): CodeSnippet {
|
||||
tool ??= deployment.tools[0]?.name
|
||||
assert(tool, 'tool is required')
|
||||
// TODO: need a way of getting example tool args
|
||||
|
@ -450,16 +488,25 @@ export function getCodeForHTTPConfig({
|
|||
const url = `${gatewayBaseUrl}/${identifier}/${tool}`
|
||||
|
||||
switch (config.httpTarget) {
|
||||
case 'curl':
|
||||
return {
|
||||
code: `curl -X POST -H "Content-Type: application/json" -d '{"query": "example google search"}' ${url}`,
|
||||
lang: 'bash'
|
||||
}
|
||||
case 'curl': {
|
||||
const formattedArgs = JSON.stringify(args).replace("'", "\\'")
|
||||
|
||||
case 'httpie':
|
||||
// TODO: better formatting for the curl command
|
||||
return {
|
||||
code: `http ${url} query='example google search'`,
|
||||
code: `curl -X POST -H "Content-Type: application/json" -d '${formattedArgs}' ${url}`,
|
||||
lang: 'bash'
|
||||
}
|
||||
}
|
||||
|
||||
case 'httpie': {
|
||||
const formattedArgs = Object.entries(args)
|
||||
.map(([key, value]) => `${key}=${JSON.stringify(value)}`)
|
||||
.join(' ')
|
||||
|
||||
return {
|
||||
code: `http ${url} ${formattedArgs}`,
|
||||
lang: 'bash'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -685,6 +685,23 @@ export interface components {
|
|||
pricingPlanOverridesMap?: {
|
||||
[key: string]: components["schemas"]["PricingPlanToolOverride"];
|
||||
};
|
||||
/** @description Examples of how to use this tool. Used to generate example usage in the tool's docs. */
|
||||
examples?: {
|
||||
/** @description The display name of the example. */
|
||||
name: string;
|
||||
/** @description The input prompt for agents to use when running this example. */
|
||||
prompt: string;
|
||||
/** @description An optional system prompt for agents to use when running this example. Defaults to `You are a helpful assistant. Be as concise as possible.` */
|
||||
systemPrompt?: string;
|
||||
/** @description The arguments to pass to the tool for this example. */
|
||||
args: {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
/** @description Whether this example should be featured in the docs for the project. */
|
||||
featured?: boolean;
|
||||
/** @description A description of the example. */
|
||||
description?: string;
|
||||
}[];
|
||||
};
|
||||
/**
|
||||
* @description Display name for the pricing plan (eg, "Free", "Starter Monthly", "Pro Annual", etc)
|
||||
|
|
|
@ -85,6 +85,62 @@ export type PricingPlanToolOverride = z.infer<
|
|||
typeof pricingPlanToolOverrideSchema
|
||||
>
|
||||
|
||||
/**
|
||||
* Example tool usage.
|
||||
*/
|
||||
export const toolConfigExampleSchema = z.object({
|
||||
/**
|
||||
* The display name of the example.
|
||||
*/
|
||||
name: z.string().describe('The display name of the example.'),
|
||||
|
||||
/**
|
||||
* The input prompt for agents to use when running this example.
|
||||
*/
|
||||
prompt: z
|
||||
.string()
|
||||
.describe('The input prompt for agents to use when running this example.'),
|
||||
|
||||
/**
|
||||
* An optional system prompt for agents to use when running this example.
|
||||
*
|
||||
* Defaults to `You are a helpful assistant. Be as concise as possible.`
|
||||
*/
|
||||
systemPrompt: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
'An optional system prompt for agents to use when running this example. Defaults to `You are a helpful assistant. Be as concise as possible.`'
|
||||
),
|
||||
|
||||
/**
|
||||
* The arguments to pass to the tool for this example.
|
||||
*/
|
||||
// TODO: validate example args against the tool's input schema during
|
||||
// config validation
|
||||
args: z
|
||||
.record(z.string(), z.any())
|
||||
.describe('The arguments to pass to the tool for this example.'),
|
||||
|
||||
/**
|
||||
* Whether this example should be featured in the docs for the project.
|
||||
*
|
||||
* The first tool with a `featured` example will be the featured tool for the
|
||||
* project.
|
||||
*/
|
||||
featured: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe(
|
||||
'Whether this example should be featured in the docs for the project.'
|
||||
),
|
||||
|
||||
/**
|
||||
* A description of the example.
|
||||
*/
|
||||
description: z.string().optional().describe('A description of the example.')
|
||||
})
|
||||
|
||||
/**
|
||||
* Customizes a tool's default behavior across all pricing plans.
|
||||
*/
|
||||
|
@ -207,11 +263,17 @@ export const toolConfigSchema = z
|
|||
.optional()
|
||||
.describe(
|
||||
"Allows you to override this tool's behavior or disable it entirely for different pricing plans. This is a map of PricingPlan slug to PricingPlanToolOverrides for that plan."
|
||||
)
|
||||
),
|
||||
|
||||
// TODO?
|
||||
// examples
|
||||
// headers
|
||||
|
||||
examples: z
|
||||
.array(toolConfigExampleSchema)
|
||||
.optional()
|
||||
.describe(
|
||||
"Examples of how to use this tool. Used to generate example usage in the tool's docs."
|
||||
)
|
||||
})
|
||||
.openapi('ToolConfig')
|
||||
|
||||
|
|
1
todo.md
1
todo.md
|
@ -121,3 +121,4 @@
|
|||
- also add `@agentic/json-schema` to `createJsonSchema` parsing instead of current no-op
|
||||
- add support for [`@google/genai`](https://github.com/googleapis/js-genai) tools adapter
|
||||
- currently difficult due to their use of non-standard json schemas
|
||||
- validate example args against the tool's input schema during config validation
|
||||
|
|
Ładowanie…
Reference in New Issue