Skip to content

Commit 59db96d

Browse files
committed
feat: update dependencies and enhance onboarding process
- Added support for the @ai-sdk/anthropic package version 2.0.0 and updated @ai-sdk/openai to version 2.0.0. - Upgraded the 'ai' package to version 5.0.0. - Enhanced the onboarding process by integrating policy fetching and risk assessment comment generation using the new anthropic SDK. - Refactored policy handling to ensure comprehensive data is used for vendor risk assessments.
1 parent 4548a0c commit 59db96d

15 files changed

Lines changed: 223 additions & 99 deletions

File tree

apps/app/package.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
"name": "@comp/app",
33
"version": "0.1.0",
44
"dependencies": {
5-
"@ai-sdk/groq": "^1.2.8",
6-
"@ai-sdk/openai": "^1.3.19",
7-
"@ai-sdk/provider": "^1.1.3",
8-
"@ai-sdk/react": "^1.2.9",
5+
"@ai-sdk/anthropic": "^2.0.0",
6+
"@ai-sdk/groq": "^2.0.0",
7+
"@ai-sdk/openai": "^2.0.0",
8+
"@ai-sdk/provider": "^2.0.0",
9+
"@ai-sdk/react": "^2.0.0",
910
"@aws-sdk/client-s3": "^3.806.0",
1011
"@aws-sdk/client-sts": "^3.808.0",
1112
"@aws-sdk/s3-request-presigner": "^3.832.0",
@@ -50,7 +51,7 @@
5051
"@uploadthing/react": "^7.3.0",
5152
"@upstash/ratelimit": "^2.0.5",
5253
"@vercel/sdk": "^1.7.1",
53-
"ai": "^4.3.16",
54+
"ai": "^5.0.0",
5455
"axios": "^1.9.0",
5556
"better-auth": "^1.2.8",
5657
"canvas-confetti": "^1.9.3",

apps/app/src/app/api/chat/route.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { tools } from '@/data/tools';
2-
import { model, type modelID } from '@/hooks/ai/providers';
32
import { auth } from '@/utils/auth';
4-
import { type UIMessage, streamText } from 'ai';
3+
import { groq } from '@ai-sdk/groq';
4+
import { type UIMessage, convertToModelMessages, streamText } from 'ai';
55
import { headers } from 'next/headers';
66

77
export const maxDuration = 30;
88

99
export async function POST(req: Request) {
10-
const { messages, selectedModel }: { messages: UIMessage[]; selectedModel: modelID } =
11-
await req.json();
10+
const { messages }: { messages: UIMessage[] } = await req.json();
1211

1312
const session = await auth.api.getSession({
1413
headers: await headers(),
@@ -31,11 +30,11 @@ export async function POST(req: Request) {
3130
`;
3231

3332
const result = streamText({
34-
model: model.languageModel(selectedModel),
33+
model: groq('deepseek-r1-distill-llama-70b'),
3534
system: systemPrompt,
36-
messages,
35+
messages: convertToModelMessages(messages),
3736
tools,
3837
});
3938

40-
return result.toDataStreamResponse({ sendReasoning: true });
39+
return result.toUIMessageStreamResponse();
4140
}

apps/app/src/components/ai/chat-text-area.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { modelID } from '@/hooks/ai/providers';
21
import { Icons } from '@comp/ui/icons';
32
import { Popover, PopoverContent, PopoverTrigger } from '@comp/ui/popover';
43
import { Textarea as ShadcnTextarea } from '@comp/ui/textarea';
@@ -10,8 +9,6 @@ interface InputProps {
109
isLoading: boolean;
1110
status: string;
1211
stop: () => void;
13-
selectedModel: modelID;
14-
setSelectedModel: (model: modelID) => void;
1512
}
1613

1714
export const ChatTextarea = ({ input, handleInputChange, isLoading }: InputProps) => {

apps/app/src/components/ai/chat.tsx

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
'use client';
22

3-
import type { modelID } from '@/hooks/ai/providers';
43
import { useSession } from '@/utils/auth-client';
54
import { useChat } from '@ai-sdk/react';
65
import { ScrollArea } from '@comp/ui/scroll-area';
6+
import { DefaultChatTransport, lastAssistantMessageIsCompleteWithToolCalls } from 'ai';
77
import { useState } from 'react';
88
import { ChatEmpty } from './chat-empty';
99
import { ChatTextarea } from './chat-text-area';
@@ -12,13 +12,15 @@ import { Messages } from './messages';
1212
export default function Chat() {
1313
const { data: session } = useSession();
1414

15-
const [selectedModel, setSelectedModel] = useState<modelID>('deepseek-r1-distill-llama-70b');
15+
const [input, setInput] = useState('');
1616

17-
const { messages, input, handleInputChange, handleSubmit, error, status, stop } = useChat({
18-
maxSteps: 5,
19-
body: {
20-
selectedModel,
21-
},
17+
const { messages, sendMessage, addToolResult, error, status, stop } = useChat({
18+
transport: new DefaultChatTransport({
19+
api: '/api/chat',
20+
}),
21+
22+
// Automatically submit when all server-side tool calls are complete
23+
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
2224
});
2325

2426
const isLoading = status === 'streaming' || status === 'submitted';
@@ -37,11 +39,17 @@ export default function Chat() {
3739
)}
3840
</ScrollArea>
3941

40-
<form onSubmit={handleSubmit}>
42+
<form
43+
onSubmit={(e) => {
44+
e.preventDefault();
45+
if (input.trim()) {
46+
sendMessage({ text: input });
47+
setInput('');
48+
}
49+
}}
50+
>
4151
<ChatTextarea
42-
selectedModel={selectedModel}
43-
setSelectedModel={setSelectedModel}
44-
handleInputChange={handleInputChange}
52+
handleInputChange={(e) => setInput(e.target.value)}
4553
input={input}
4654
isLoading={isLoading}
4755
status={status}

apps/app/src/components/ai/message.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { useStreamableText } from '@/hooks/use-streamable-text';
44
import { cn } from '@comp/ui/cn';
5-
import type { Message as TMessage } from 'ai';
5+
import type { UIMessage } from 'ai';
66
import type { StreamableValue } from 'ai/rsc';
77
import equal from 'fast-deep-equal';
88
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react';
@@ -26,8 +26,8 @@ interface ExtendedToolInvocation extends ToolInvocation {
2626

2727
interface ReasoningPart {
2828
type: 'reasoning';
29-
reasoning: string;
30-
details: Array<{ type: 'text'; text: string }>;
29+
text: string;
30+
details?: Array<{ type: 'text'; text: string }>;
3131
}
3232

3333
interface ReasoningMessagePartProps {
@@ -117,13 +117,13 @@ export function ReasoningMessagePart({ part, isReasoning }: ReasoningMessagePart
117117
variants={variants}
118118
transition={{ duration: 0.2, ease: 'easeInOut' }}
119119
>
120-
{part.details.map((detail) =>
120+
{part.details?.map((detail) =>
121121
detail.type === 'text' ? (
122122
<StreamableMarkdown key={detail.text} text={detail.text} />
123123
) : (
124124
'<redacted>'
125125
),
126-
)}
126+
) || <StreamableMarkdown text={part.text} />}
127127
</motion.div>
128128
)}
129129
</AnimatePresence>
@@ -159,7 +159,7 @@ const PurePreviewMessage = ({
159159
isLatestMessage,
160160
status,
161161
}: {
162-
message: TMessage;
162+
message: UIMessage;
163163
isLoading: boolean;
164164
status: 'error' | 'submitted' | 'streaming' | 'ready';
165165
isLatestMessage: boolean;
@@ -180,7 +180,12 @@ const PurePreviewMessage = ({
180180
)}
181181
>
182182
<div className="flex w-full flex-col space-y-4">
183-
{message.parts?.map((part, i) => {
183+
{message.parts?.map((part: any, i: number) => {
184+
// Skip invalid parts
185+
if (!part || !part.type) {
186+
return null;
187+
}
188+
184189
switch (part.type) {
185190
case 'text':
186191
return message.role === 'user' ? (
@@ -196,7 +201,7 @@ const PurePreviewMessage = ({
196201
message.role === 'user',
197202
})}
198203
>
199-
<StreamableMarkdown text={part.text} />
204+
<StreamableMarkdown text={part.text || ''} />
200205
</div>
201206
</motion.div>
202207
) : (
@@ -207,7 +212,7 @@ const PurePreviewMessage = ({
207212
className="flex w-full flex-row items-start gap-2 pb-2"
208213
>
209214
<BotCard key={`message-${message.id}-part-${i}`}>
210-
<StreamableMarkdown text={part.text} />
215+
<StreamableMarkdown text={part.text || ''} />
211216
</BotCard>
212217
</motion.div>
213218
);
@@ -216,8 +221,7 @@ const PurePreviewMessage = ({
216221
return (
217222
<ReasoningMessagePart
218223
key={`message-${message.id}-${i}`}
219-
// @ts-expect-error part
220-
part={part}
224+
part={part as ReasoningPart}
221225
isReasoning={
222226
(message.parts &&
223227
status === 'streaming' &&
@@ -227,7 +231,9 @@ const PurePreviewMessage = ({
227231
/>
228232
);
229233
}
234+
230235
default:
236+
// Skip tool parts - only show final text response
231237
return null;
232238
}
233239
})}
@@ -303,8 +309,6 @@ export function UserMessage({ content }: { content: string }) {
303309
export const Message = memo(PurePreviewMessage, (prevProps, nextProps) => {
304310
if (prevProps.status !== nextProps.status) return false;
305311

306-
if (prevProps.message.annotations !== nextProps.message.annotations) return false;
307-
308312
if (!equal(prevProps.message.parts, nextProps.message.parts)) return false;
309313

310314
return true;

apps/app/src/components/ai/messages.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { useScrollToBottom } from '@/hooks/use-scroll-to-bottom';
2-
import type { Message as TMessage } from 'ai';
2+
import type { UIMessage } from 'ai';
33
import { Message } from './message';
44

55
export const Messages = ({
66
messages,
77
isLoading,
88
status,
99
}: {
10-
messages: TMessage[];
10+
messages: UIMessage[];
1111
isLoading: boolean;
1212
status: 'error' | 'submitted' | 'streaming' | 'ready';
1313
}) => {

apps/app/src/data/tools/organization.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { auth } from '@/utils/auth';
22
import { db } from '@db';
3-
import { tool } from 'ai';
43
import { headers } from 'next/headers';
54
import { z } from 'zod';
65

@@ -10,9 +9,9 @@ export function getOrganizationTools() {
109
};
1110
}
1211

13-
export const findOrganization = tool({
12+
export const findOrganization = {
1413
description: "Find the users organization and it's details",
15-
parameters: z.object({}),
14+
inputSchema: z.object({}),
1615
execute: async () => {
1716
const session = await auth.api.getSession({
1817
headers: await headers(),
@@ -40,4 +39,4 @@ export const findOrganization = tool({
4039
organization: org,
4140
};
4241
},
43-
});
42+
};

apps/app/src/data/tools/policies.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { auth } from '@/utils/auth';
22
import { db } from '@db';
3-
import { tool } from 'ai';
43
import { headers } from 'next/headers';
54
import { z } from 'zod';
65

@@ -11,12 +10,12 @@ export function getPolicyTools() {
1110
};
1211
}
1312

14-
export const getPolicies = tool({
13+
export const getPolicies = {
1514
description: 'Get all policies for the organization',
16-
parameters: z.object({
15+
inputSchema: z.object({
1716
status: z.enum(['draft', 'published']).optional(),
1817
}),
19-
execute: async ({ status }) => {
18+
execute: async ({ status }: { status?: 'draft' | 'published' }) => {
2019
const session = await auth.api.getSession({
2120
headers: await headers(),
2221
});
@@ -49,15 +48,15 @@ export const getPolicies = tool({
4948
policies,
5049
};
5150
},
52-
});
51+
};
5352

54-
export const getPolicyContent = tool({
53+
export const getPolicyContent = {
5554
description:
5655
'Get the content of a specific policy by id. We can only acquire the policy id by running the getPolicies tool first.',
57-
parameters: z.object({
56+
inputSchema: z.object({
5857
id: z.string(),
5958
}),
60-
execute: async ({ id }) => {
59+
execute: async ({ id }: { id: string }) => {
6160
const session = await auth.api.getSession({
6261
headers: await headers(),
6362
});
@@ -84,4 +83,4 @@ export const getPolicyContent = tool({
8483
content: policy?.content,
8584
};
8685
},
87-
});
86+
};

apps/app/src/data/tools/risks-tool.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { auth } from '@/utils/auth';
22
import { db, Departments, RiskCategory, RiskStatus } from '@db';
3-
import { tool } from 'ai';
43
import { headers } from 'next/headers';
54
import { z } from 'zod';
65

@@ -11,15 +10,25 @@ export function getRiskTools() {
1110
};
1211
}
1312

14-
export const getRisks = tool({
13+
export const getRisks = {
1514
description: 'Get risks for the organization',
16-
parameters: z.object({
15+
inputSchema: z.object({
1716
status: z.enum(Object.values(RiskStatus) as [RiskStatus, ...RiskStatus[]]).optional(),
1817
department: z.enum(Object.values(Departments) as [Departments, ...Departments[]]).optional(),
1918
category: z.enum(Object.values(RiskCategory) as [RiskCategory, ...RiskCategory[]]).optional(),
2019
owner: z.string().optional(),
2120
}),
22-
execute: async ({ status, department, category, owner }) => {
21+
execute: async ({
22+
status,
23+
department,
24+
category,
25+
owner,
26+
}: {
27+
status?: RiskStatus;
28+
department?: Departments;
29+
category?: RiskCategory;
30+
owner?: string;
31+
}) => {
2332
const session = await auth.api.getSession({
2433
headers: await headers(),
2534
});
@@ -54,14 +63,14 @@ export const getRisks = tool({
5463
risks,
5564
};
5665
},
57-
});
66+
};
5867

59-
export const getRiskById = tool({
68+
export const getRiskById = {
6069
description: 'Get a risk by id',
61-
parameters: z.object({
70+
inputSchema: z.object({
6271
id: z.string(),
6372
}),
64-
execute: async ({ id }) => {
73+
execute: async ({ id }: { id: string }) => {
6574
const session = await auth.api.getSession({
6675
headers: await headers(),
6776
});
@@ -85,4 +94,4 @@ export const getRiskById = tool({
8594
risk,
8695
};
8796
},
88-
});
97+
};

0 commit comments

Comments
 (0)