kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat(web): improving stuff and things
rodzic
2e16139860
commit
c3d39f1d62
|
@ -8,6 +8,7 @@ import { toast } from 'sonner'
|
|||
import { useAuthenticatedAgentic } from '@/components/agentic-provider'
|
||||
import { useConfettiFireworks } from '@/components/confetti'
|
||||
import { LoadingIndicator } from '@/components/loading-indicator'
|
||||
import { PageContainer } from '@/components/page-container'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { toastError } from '@/lib/notifications'
|
||||
import { useQuery } from '@/lib/query-client'
|
||||
|
@ -93,38 +94,42 @@ export function AppConsumerIndex({ consumerId }: { consumerId: string }) {
|
|||
}, [ctx, consumer])
|
||||
|
||||
return (
|
||||
<section className='flex flex-col gap-16'>
|
||||
{!ctx || isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : isError ? (
|
||||
<p>Error fetching customer subscription "{consumerId}"</p>
|
||||
) : !consumer ? (
|
||||
<p>Customer subscription "{consumerId}" not found</p>
|
||||
) : (
|
||||
<>
|
||||
<h1
|
||||
className='text-center text-balance leading-snug md:leading-none
|
||||
<PageContainer>
|
||||
<section className='flex flex-col gap-16'>
|
||||
{!ctx || isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : isError ? (
|
||||
<p>Error fetching customer subscription "{consumerId}"</p>
|
||||
) : !consumer ? (
|
||||
<p>Customer subscription "{consumerId}" not found</p>
|
||||
) : (
|
||||
<>
|
||||
<h1
|
||||
className='text-center text-balance leading-snug md:leading-none
|
||||
text-4xl font-extrabold'
|
||||
>
|
||||
Subscription to {consumer.project.name}
|
||||
</h1>
|
||||
>
|
||||
Subscription to {consumer.project.name}
|
||||
</h1>
|
||||
|
||||
<div className=''>
|
||||
<pre className='max-w-lg'>{JSON.stringify(consumer, null, 2)}</pre>
|
||||
</div>
|
||||
<div className=''>
|
||||
<pre className='max-w-lg'>
|
||||
{JSON.stringify(consumer, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={onManageSubscription}
|
||||
disabled={isLoadingStripeBillingPortal}
|
||||
>
|
||||
{isLoadingStripeBillingPortal && (
|
||||
<Loader2Icon className='animate-spin mr-2' />
|
||||
)}
|
||||
<Button
|
||||
onClick={onManageSubscription}
|
||||
disabled={isLoadingStripeBillingPortal}
|
||||
>
|
||||
{isLoadingStripeBillingPortal && (
|
||||
<Loader2Icon className='animate-spin mr-2' />
|
||||
)}
|
||||
|
||||
<span>Manage Subscription</span>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
<span>Manage Subscription</span>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
</PageContainer>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { useCallback, useState } from 'react'
|
|||
|
||||
import { useAuthenticatedAgentic } from '@/components/agentic-provider'
|
||||
import { AppConsumersList } from '@/components/app-consumers-list'
|
||||
import { PageContainer } from '@/components/page-container'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { toastError } from '@/lib/notifications'
|
||||
|
||||
|
@ -31,7 +32,7 @@ export function AppConsumersIndex() {
|
|||
}, [ctx])
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageContainer>
|
||||
<h1
|
||||
className='text-center text-balance leading-snug md:leading-none
|
||||
text-4xl font-extrabold'
|
||||
|
@ -48,6 +49,6 @@ export function AppConsumersIndex() {
|
|||
</Button>
|
||||
|
||||
<AppConsumersList />
|
||||
</>
|
||||
</PageContainer>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { useAuthenticatedAgentic } from '@/components/agentic-provider'
|
||||
import { LoadingIndicator } from '@/components/loading-indicator'
|
||||
import { PageContainer } from '@/components/page-container'
|
||||
import { useQuery } from '@/lib/query-client'
|
||||
|
||||
export function AppProjectIndex({
|
||||
|
@ -27,27 +28,29 @@ export function AppProjectIndex({
|
|||
// TODO: show deployments
|
||||
|
||||
return (
|
||||
<section>
|
||||
{!ctx || isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : isError ? (
|
||||
<p>Error fetching project</p>
|
||||
) : !project ? (
|
||||
<p>Project "{projectIdentifier}" not found</p>
|
||||
) : (
|
||||
<>
|
||||
<h1
|
||||
className='text-center text-balance leading-snug md:leading-none
|
||||
<PageContainer>
|
||||
<section>
|
||||
{!ctx || isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : isError ? (
|
||||
<p>Error fetching project</p>
|
||||
) : !project ? (
|
||||
<p>Project "{projectIdentifier}" not found</p>
|
||||
) : (
|
||||
<>
|
||||
<h1
|
||||
className='text-center text-balance leading-snug md:leading-none
|
||||
text-4xl font-extrabold'
|
||||
>
|
||||
{project.name}
|
||||
</h1>
|
||||
>
|
||||
{project.name}
|
||||
</h1>
|
||||
|
||||
<div className='mt-8'>
|
||||
<pre className='max-w-lg'>{JSON.stringify(project, null, 2)}</pre>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
<div className='mt-8'>
|
||||
<pre className='max-w-lg'>{JSON.stringify(project, null, 2)}</pre>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
</PageContainer>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { AppProjectsList } from '@/components/app-projects-list'
|
||||
import { PageContainer } from '@/components/page-container'
|
||||
|
||||
export function AppProjectsIndex() {
|
||||
return (
|
||||
<>
|
||||
<PageContainer>
|
||||
<section>
|
||||
<div className='flex gap-8 space-around'>
|
||||
<AppProjectsList />
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
</PageContainer>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ export default function RootLayout({
|
|||
<div className='relative w-full min-h-[100vh] flex flex-col items-center'>
|
||||
<Header />
|
||||
|
||||
<main className='relative w-full flex-1 flex flex-col items-center gap-16 py-16 px-2 overflow-hidden'>
|
||||
<main className='relative w-full flex-1 flex flex-col items-center gap-16 pt-8 pb-16 px-2 overflow-hidden'>
|
||||
{children}
|
||||
</main>
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator
|
||||
} from '@/components/ui/breadcrumb'
|
||||
|
||||
export function MarketplacePublicProjectDetailNav({
|
||||
projectIdentifier
|
||||
}: {
|
||||
projectIdentifier: string
|
||||
}) {
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href='/marketplace'>Marketplace</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
|
||||
<BreadcrumbSeparator />
|
||||
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>{projectIdentifier}</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
)
|
||||
}
|
|
@ -30,6 +30,7 @@ import { defaultAgenticApiClient } from '@/lib/default-agentic-api-client'
|
|||
import { toast, toastError } from '@/lib/notifications'
|
||||
import { useQuery } from '@/lib/query-client'
|
||||
|
||||
// import { MarketplacePublicProjectDetailNav } from './marketplace-nav'
|
||||
import {
|
||||
type MarketplacePublicProjectDetailTab,
|
||||
marketplacePublicProjectDetailTabsSet,
|
||||
|
@ -200,7 +201,7 @@ export function MarketplacePublicProjectDetail({
|
|||
}, [deployment])
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<PageContainer compact>
|
||||
<section>
|
||||
{isLoading ? (
|
||||
<LoadingIndicator />
|
||||
|
@ -484,73 +485,79 @@ function ProjectHeader({
|
|||
tab?: MarketplacePublicProjectDetailTab
|
||||
}) {
|
||||
return (
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='w-full flex flex-row gap-2.5 items-center'>
|
||||
<img
|
||||
src={
|
||||
project.lastPublishedDeployment?.iconUrl ||
|
||||
project.user?.image ||
|
||||
'/agentic-icon-circle-light.svg'
|
||||
}
|
||||
alt={project.name}
|
||||
className='aspect-square w-10 h-10'
|
||||
/>
|
||||
<>
|
||||
{/* <MarketplacePublicProjectDetailNav
|
||||
projectIdentifier={project.identifier}
|
||||
/> */}
|
||||
|
||||
<h1 className='flex-1 font-semibold text-balance text-3xl leading-tight'>
|
||||
{project.name}
|
||||
</h1>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='w-full flex flex-row gap-2.5 items-center'>
|
||||
<img
|
||||
src={
|
||||
project.lastPublishedDeployment?.iconUrl ||
|
||||
project.user?.image ||
|
||||
'/agentic-icon-circle-light.svg'
|
||||
}
|
||||
alt={project.name}
|
||||
className='aspect-square w-10 h-10'
|
||||
/>
|
||||
|
||||
<HeroButton
|
||||
heroVariant='orange'
|
||||
className='justify-self-end'
|
||||
disabled={tab === 'pricing'}
|
||||
asChild={tab !== 'pricing'}
|
||||
>
|
||||
<Link
|
||||
href={`/marketplace/projects/${project.identifier}?tab=pricing`}
|
||||
<h1 className='flex-1 font-semibold text-balance text-3xl leading-tight'>
|
||||
{project.name}
|
||||
</h1>
|
||||
|
||||
<HeroButton
|
||||
heroVariant='orange'
|
||||
className='justify-self-end'
|
||||
disabled={tab === 'pricing'}
|
||||
asChild={tab !== 'pricing'}
|
||||
>
|
||||
Subscribe to {project.identifier}
|
||||
</Link>
|
||||
</HeroButton>
|
||||
</div>
|
||||
|
||||
<div className='flex flex-row items-center'>
|
||||
<div className='text-sm text-muted-foreground flex flex-row gap-0.5 items-center hover:no-underline! no-underline!'>
|
||||
<span>{project.identifier}</span>
|
||||
|
||||
{/* TODO: <CopyIcon className='w-4 h-4' /> */}
|
||||
<Link
|
||||
href={`/marketplace/projects/${project.identifier}?tab=pricing`}
|
||||
>
|
||||
Subscribe to {project.identifier}
|
||||
</Link>
|
||||
</HeroButton>
|
||||
</div>
|
||||
|
||||
{project.lastPublishedDeployment?.homepageUrl && (
|
||||
<Button asChild variant='link'>
|
||||
<Link
|
||||
href={project.lastPublishedDeployment.homepageUrl}
|
||||
className='text-sm flex flex-row gap-1.5! items-center text-muted-foreground! py-1! px-2!'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<ExternalLinkIcon className='w-4 h-4' />
|
||||
<div className='flex flex-row items-center'>
|
||||
<div className='text-sm text-muted-foreground flex flex-row gap-0.5 items-center hover:no-underline! no-underline!'>
|
||||
<span>{project.identifier}</span>
|
||||
|
||||
<span>Homepage</span>
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
{/* TODO: <CopyIcon className='w-4 h-4' /> */}
|
||||
</div>
|
||||
|
||||
{project.lastPublishedDeployment?.sourceUrl && (
|
||||
<Button asChild variant='link'>
|
||||
<Link
|
||||
href={project.lastPublishedDeployment.sourceUrl}
|
||||
className='text-sm flex flex-row gap-1.5! items-center text-muted-foreground! py-1! px-2!'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<GitHubIcon className='w-4 h-4' />
|
||||
{project.lastPublishedDeployment?.homepageUrl && (
|
||||
<Button asChild variant='link'>
|
||||
<Link
|
||||
href={project.lastPublishedDeployment.homepageUrl}
|
||||
className='text-sm flex flex-row gap-1.5! items-center text-muted-foreground! py-1! px-2!'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<ExternalLinkIcon className='w-4 h-4' />
|
||||
|
||||
<span>GitHub</span>
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
<span>Homepage</span>
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{project.lastPublishedDeployment?.sourceUrl && (
|
||||
<Button asChild variant='link'>
|
||||
<Link
|
||||
href={project.lastPublishedDeployment.sourceUrl}
|
||||
className='text-sm flex flex-row gap-1.5! items-center text-muted-foreground! py-1! px-2!'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<GitHubIcon className='w-4 h-4' />
|
||||
|
||||
<span>GitHub</span>
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@ import { cn } from '@/lib/utils'
|
|||
|
||||
export function PageContainer({
|
||||
background = true,
|
||||
compact = false,
|
||||
className,
|
||||
children
|
||||
}: {
|
||||
background?: boolean
|
||||
compact?: boolean
|
||||
className?: string
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
|
@ -18,6 +20,7 @@ export function PageContainer({
|
|||
<div
|
||||
className={cn(
|
||||
'relative w-full flex-1 flex flex-col items-center max-w-[1200px] gap-16 z-10',
|
||||
compact ? 'pt-4' : 'pt-8',
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
import { Slot } from '@radix-ui/react-slot'
|
||||
import { ChevronRight, MoreHorizontal } from 'lucide-react'
|
||||
import * as React from 'react'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) {
|
||||
return <nav aria-label='breadcrumb' data-slot='breadcrumb' {...props} />
|
||||
}
|
||||
|
||||
function BreadcrumbList({ className, ...props }: React.ComponentProps<'ol'>) {
|
||||
return (
|
||||
<ol
|
||||
data-slot='breadcrumb-list'
|
||||
className={cn(
|
||||
'text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function BreadcrumbItem({ className, ...props }: React.ComponentProps<'li'>) {
|
||||
return (
|
||||
<li
|
||||
data-slot='breadcrumb-item'
|
||||
className={cn('inline-flex items-center gap-1.5', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function BreadcrumbLink({
|
||||
asChild,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<'a'> & {
|
||||
asChild?: boolean
|
||||
}) {
|
||||
const Comp = asChild ? Slot : 'a'
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot='breadcrumb-link'
|
||||
className={cn('hover:text-foreground transition-colors', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function BreadcrumbPage({ className, ...props }: React.ComponentProps<'span'>) {
|
||||
return (
|
||||
<span
|
||||
data-slot='breadcrumb-page'
|
||||
role='link'
|
||||
aria-disabled='true'
|
||||
aria-current='page'
|
||||
className={cn('text-foreground font-normal', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function BreadcrumbSeparator({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<'li'>) {
|
||||
return (
|
||||
<li
|
||||
data-slot='breadcrumb-separator'
|
||||
role='presentation'
|
||||
aria-hidden='true'
|
||||
className={cn('[&>svg]:size-3.5', className)}
|
||||
{...props}
|
||||
>
|
||||
{children ?? <ChevronRight />}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
function BreadcrumbEllipsis({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<'span'>) {
|
||||
return (
|
||||
<span
|
||||
data-slot='breadcrumb-ellipsis'
|
||||
role='presentation'
|
||||
aria-hidden='true'
|
||||
className={cn('flex size-9 items-center justify-center', className)}
|
||||
{...props}
|
||||
>
|
||||
<MoreHorizontal className='size-4' />
|
||||
<span className='sr-only'>More</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Breadcrumb,
|
||||
BreadcrumbEllipsis,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator
|
||||
}
|
|
@ -317,6 +317,7 @@ const result = await generateText({
|
|||
model: openai('gpt-4o-mini'),
|
||||
tools: createAISDKTools(searchTool),
|
||||
toolChoice: 'required',
|
||||
system: '${systemPrompt}',
|
||||
prompt: '${prompt}'
|
||||
})
|
||||
|
||||
|
@ -416,9 +417,10 @@ const searchTool = await AgenticToolClient.fromIdentifier('${identifier}'${
|
|||
: ''
|
||||
})
|
||||
|
||||
const tools = createLangChainTools(searchTool)
|
||||
const agent = createToolCallingAgent({
|
||||
llm: new ChatOpenAI({ model: 'gpt-4o-mini' }),
|
||||
tools: createLangChainTools(searchTool),
|
||||
tools,
|
||||
prompt: ChatPromptTemplate.fromMessages([
|
||||
['placeholder', '{chat_history}'],
|
||||
['human', '{input}'],
|
||||
|
|
|
@ -2,12 +2,11 @@ import 'dotenv/config'
|
|||
|
||||
import { createAISDKTools } from '@agentic/ai-sdk'
|
||||
import { AgenticToolClient } from '@agentic/platform-tool-client'
|
||||
import { createOpenAI } from '@ai-sdk/openai'
|
||||
import { openai } from '@ai-sdk/openai'
|
||||
import { generateText } from 'ai'
|
||||
|
||||
async function main() {
|
||||
const searchTool = await AgenticToolClient.fromIdentifier('@agentic/search')
|
||||
const openai = createOpenAI({ compatibility: 'strict' })
|
||||
|
||||
const result = await generateText({
|
||||
model: openai('gpt-4o-mini'),
|
||||
|
|
|
@ -10,9 +10,9 @@ async function main() {
|
|||
|
||||
const weatherAgent = new Agent({
|
||||
name: 'Weather Agent',
|
||||
instructions: 'You are a helpful assistant. Be as concise as possible.',
|
||||
model: openai('gpt-4o-mini'),
|
||||
tools: createMastraTools(searchTool)
|
||||
tools: createMastraTools(searchTool),
|
||||
instructions: 'You are a helpful assistant. Be as concise as possible.'
|
||||
})
|
||||
|
||||
const res = await weatherAgent.generate(
|
||||
|
|
|
@ -109,7 +109,7 @@ catalogs:
|
|||
specifier: ^2.1.7
|
||||
version: 2.1.7
|
||||
'@radix-ui/react-slot':
|
||||
specifier: ^1.2.3
|
||||
specifier: 1.2.3
|
||||
version: 1.2.3
|
||||
'@radix-ui/react-tabs':
|
||||
specifier: ^1.1.12
|
||||
|
|
|
@ -45,7 +45,7 @@ catalog:
|
|||
'@radix-ui/react-collapsible': ^1.1.11
|
||||
'@radix-ui/react-dropdown-menu': 2.1.15
|
||||
'@radix-ui/react-label': ^2.1.7
|
||||
'@radix-ui/react-slot': ^1.2.3
|
||||
'@radix-ui/react-slot': 1.2.3
|
||||
'@radix-ui/react-tabs': ^1.1.12
|
||||
'@radix-ui/react-tooltip': ^1.2.7
|
||||
'@react-email/components': ^0.1.1
|
||||
|
|
3
todo.md
3
todo.md
|
@ -10,9 +10,6 @@
|
|||
- double check stripe upgrade flow and add fireworks
|
||||
- improve upgrade flow UX
|
||||
- should we bypass stripe for `free` plans to increase conversions?
|
||||
- example usage
|
||||
- double check example usage for all TS sdks now that real examples are working
|
||||
- fix mcp examples
|
||||
- **replace json pricing plans and consumers with actual designs**
|
||||
- double-check free-tier rate-limits for `@agentic/search`
|
||||
- docs: add notes about constraints on mcp origin servers (static tools)
|
||||
|
|
Ładowanie…
Reference in New Issue