Skip to content

Commit 2eb35f9

Browse files
Bran18AndlerRLcoderabbitai[bot]
authored
[masterbots.ai] feat: wordware api (#276)
* feat: add wordware api + vercel sdk strategy * feat: add wordware api + vercel sdk * wordware describe feat * wordware run + interface * impr(masterbots.ai): upt /api/wordware/describe/route.ts coderabbitai code suggestion. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * impr(masterbots.ai): upt /api/wordware/describe/route.ts coderabbitai code suggestion. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(masterbots.ai): typo /api/wordware/describe/route.ts coderabbitai code suggestion. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: Roberto Lucas <andler@bitcash.org> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent 14ee814 commit 2eb35f9

File tree

10 files changed

+536
-2
lines changed

10 files changed

+536
-2
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { NextResponse } from 'next/server'
2+
3+
export async function GET(request: Request) {
4+
const { searchParams } = new URL(request.url)
5+
const promptId = searchParams.get('promptId')
6+
const apiKey = process.env.WORDWARE_API_KEY
7+
8+
if (!apiKey) {
9+
console.error('Wordware API key is not set')
10+
return NextResponse.json(
11+
{ error: 'Internal server error' },
12+
{ status: 500 }
13+
)
14+
}
15+
16+
if (!promptId) {
17+
return NextResponse.json(
18+
{ error: 'Invalid or missing promptId' },
19+
{ status: 400 }
20+
)
21+
}
22+
23+
try {
24+
const response = await fetch(
25+
`https://app.wordware.ai/api/prompt/${promptId}/describe`,
26+
{
27+
headers: {
28+
Authorization: `Bearer ${apiKey}`
29+
}
30+
}
31+
)
32+
33+
const contentType = response.headers.get('content-type')
34+
if (contentType && contentType.indexOf('application/json') !== -1) {
35+
const data = await response.json()
36+
return NextResponse.json(data, { status: response.status })
37+
} else {
38+
const text = await response.text()
39+
return NextResponse.json(
40+
{
41+
error: 'Unexpected response from Wordware API',
42+
details: text.substring(0, 200)
43+
},
44+
{ status: response.status }
45+
)
46+
}
47+
} catch (error) {
48+
console.error('Error fetching prompt details:', error)
49+
return NextResponse.json(
50+
{
51+
error: 'An error occurred while fetching prompt details',
52+
details: error
53+
},
54+
{ status: 500 }
55+
)
56+
}
57+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
export const wordwareModel = (API_KEY: string) => ({
3+
id: 'wordware',
4+
name: 'Wordware AI',
5+
specificationVersion: 'v1',
6+
provider: 'wordware',
7+
modelId: 'wordware-ai',
8+
stream: async function* (opts: { messages: Array<{ role: string; content: string }> }) {
9+
const { promptId, inputs } = JSON.parse(opts.messages[0].content);
10+
11+
const response = await fetch(`https://app.wordware.ai/api/prompt/${promptId}/run`, {
12+
method: 'POST',
13+
headers: {
14+
'Authorization': `Bearer ${API_KEY}`,
15+
'Content-Type': 'application/json',
16+
},
17+
body: JSON.stringify({ inputs }),
18+
});
19+
20+
if (!response.ok) {
21+
throw new Error(`Wordware API error: ${response.statusText}`);
22+
}
23+
24+
const reader = response.body!.getReader();
25+
const decoder = new TextDecoder();
26+
27+
while (true) {
28+
const { done, value } = await reader.read();
29+
if (done) break;
30+
yield decoder.decode(value);
31+
}
32+
},
33+
defaultObjectGenerationMode: 'tool',
34+
tokenizerEncode: async () => [], // Implement if needed
35+
tokenizerDecode: async () => '', // Implement if needed
36+
});

apps/masterbots.ai/app/api/wordware/run/.route.ts.swp

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// app/api/wordware/run/route.ts
2+
3+
import { NextResponse } from 'next/server';
4+
5+
export async function POST(req: Request) {
6+
const API_KEY = process.env.WORDWARE_API_KEY;
7+
8+
if (!API_KEY) {
9+
return NextResponse.json({ error: 'Wordware API key is not set' }, { status: 500 });
10+
}
11+
12+
try {
13+
const { promptId, inputs } = await req.json();
14+
15+
const response = await fetch(`https://app.wordware.ai/api/prompt/${promptId}/run`, {
16+
method: 'POST',
17+
headers: {
18+
'Authorization': `Bearer ${API_KEY}`,
19+
'Content-Type': 'application/json',
20+
},
21+
body: JSON.stringify({ inputs }),
22+
});
23+
24+
if (!response.ok) {
25+
throw new Error(`Wordware API error: ${response.statusText}`);
26+
}
27+
28+
// Instead of parsing JSON, we'll return the response as a stream
29+
return new NextResponse(response.body, {
30+
headers: {
31+
'Content-Type': 'text/event-stream',
32+
'Cache-Control': 'no-cache',
33+
'Connection': 'keep-alive',
34+
},
35+
});
36+
} catch (error) {
37+
console.error('Error running Wordware prompt:', error);
38+
return NextResponse.json({ error: 'An error occurred while running the prompt' }, { status: 500 });
39+
}
40+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import FooterCT from '@/components/layout/footer/footer-ct'
2+
import { ResponsiveSidebar } from '@/components/layout/sidebar/sidebar-responsive'
3+
import { ChatLayoutSection } from '@/components/routes/chat/chat-layout-section'
4+
import NextTopLoader from 'nextjs-toploader'
5+
6+
interface ChatLayoutProps {
7+
children: React.ReactNode
8+
}
9+
10+
export default async function ChatLayout({ children }: ChatLayoutProps) {
11+
return (
12+
<main className="relative flex flex-col h-[calc(100vh-4rem)] overflow-hidden">
13+
<NextTopLoader color="#1ED761" initialPosition={0.2} />
14+
<ResponsiveSidebar />
15+
<ChatLayoutSection>{children}</ChatLayoutSection>
16+
<div className="layout-footer">
17+
<FooterCT />
18+
</div>
19+
</main>
20+
)
21+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { authOptions } from '@/auth'
2+
import { generateMetadataFromSEO } from '@/lib/metadata'
3+
import { isTokenExpired } from 'mb-lib'
4+
import { Metadata } from 'next'
5+
import { getServerSession } from 'next-auth'
6+
import { redirect } from 'next/navigation'
7+
import {WordwareChat} from '@/components/shared/wordware-chat';
8+
9+
export default async function IndexPage() {
10+
const session = await getServerSession(authOptions)
11+
12+
// NOTE: maybe we should use same expiration time
13+
const jwt = session?.user?.hasuraJwt
14+
15+
if (!jwt || isTokenExpired(jwt)) {
16+
redirect('/auth/signin')
17+
}
18+
19+
20+
return (
21+
<>
22+
<WordwareChat />
23+
</>
24+
)
25+
}
26+
27+
export async function generateMetadata(): Promise<Metadata> {
28+
const seoData = {
29+
title: 'Wordware page',
30+
description:
31+
'Welcome to the chatbot page. Interact with our AI-powered chatbot and get answers to your questions.',
32+
ogType: 'website',
33+
ogImageUrl: '',
34+
twitterCard: 'summary'
35+
}
36+
37+
return generateMetadataFromSEO(seoData)
38+
}

apps/masterbots.ai/components/layout/header/header.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export function Header() {
1818
<IconSeparator className="size-6 text-muted-foreground/50" />
1919
<HeaderLink href="/c" text="Chat" />
2020
<HeaderLink href="/" text="Browse" />
21+
<HeaderLink href="/wordware" text="Ww" />
22+
2123
{appConfig.devMode && (
2224
<HeaderLink href="/c/p" text="Pro" />
2325
)}

0 commit comments

Comments
 (0)