-
Notifications
You must be signed in to change notification settings - Fork 0
feat: ai agent #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: ai agent #12
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,2 @@ | ||
| auto-install-peers=true | ||
| shamefully-hoist=true | ||
| node-linker=hoisted |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,24 @@ | ||
| # Session password for Nuxt Auth Utils | ||
| NUXT_SESSION_PASSWORD="" | ||
| NUXT_SESSION_PASSWORD= | ||
|
|
||
| # Main database | ||
| DATABASE_URL="" | ||
| DATABASE_URL= | ||
|
|
||
| # S3 file storage | ||
| NUXT_S3_BUCKET="" | ||
| NUXT_S3_REGION="" | ||
| NUXT_S3_ENDPOINT="" | ||
| NUXT_S3_ACCESS_KEY_ID="" | ||
| NUXT_S3_SECRET_ACCESS_KEY="" | ||
| NUXT_S3_ACCESS_KEY_ID= | ||
| NUXT_S3_BUCKET= | ||
| NUXT_S3_ENDPOINT= | ||
| NUXT_S3_REGION= | ||
| NUXT_S3_SECRET_ACCESS_KEY= | ||
|
|
||
| # URL to media server (probably s3 bucket with static URL) | ||
| NUXT_PUBLIC_MEDIA_URL="" | ||
| NUXT_PUBLIC_MEDIA_URL= | ||
|
|
||
| # AI | ||
| NUXT_AI_API_KEY= | ||
| NUXT_AI_BASE_URL= | ||
| NUXT_AI_MODEL= | ||
| NUXT_AI_SERVICE_TOKEN= | ||
|
|
||
| # App version | ||
| VERSION="" | ||
| VERSION= |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| import { Buffer } from 'node:buffer' | ||
| import { timingSafeEqual } from 'node:crypto' | ||
| import { Agent, OpenAIChatCompletionsModel, run } from '@openai/agents' | ||
| import OpenAI from 'openai' | ||
| import { getPartnersByCityTool, getPartnersTool } from '~~/server/services/tools' | ||
|
|
||
| export default defineEventHandler(async (event) => { | ||
| try { | ||
| const { ai } = useRuntimeConfig() | ||
|
|
||
| // Requires bearer token | ||
| const bearer = getHeader(event, 'authorization') | ||
| if (!bearer?.startsWith('Bearer ')) { | ||
| throw createError({ | ||
| statusCode: 401, | ||
| message: 'Unauthorized', | ||
| }) | ||
| } | ||
|
|
||
| const token = bearer.slice(7) // Remove 'Bearer ' prefix | ||
| if (!ai.serviceToken || !timingSafeEqual(Buffer.from(token), Buffer.from(ai.serviceToken))) { | ||
| throw createError({ | ||
| statusCode: 401, | ||
| message: 'Unauthorized', | ||
| }) | ||
| } | ||
|
|
||
| const body = await readBody(event) | ||
| if (!body?.message) { | ||
| throw createError({ | ||
| statusCode: 400, | ||
| message: 'Message is required', | ||
| }) | ||
| } | ||
|
|
||
| const client = new OpenAI({ | ||
| apiKey: ai.apiKey, | ||
| baseURL: ai.baseUrl, | ||
| }) | ||
|
Comment on lines
+36
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add OpenAI client configuration validation. The OpenAI client is created without validating the required configuration values, which could lead to runtime errors. Add configuration validation: +if (!ai.apiKey || !ai.baseUrl || !ai.model) {
+ throw createError({
+ statusCode: 500,
+ message: 'AI service configuration is incomplete',
+ })
+}
const client = new OpenAI({
apiKey: ai.apiKey,
baseURL: ai.baseUrl,
})🤖 Prompt for AI Agents |
||
|
|
||
| const agent = new Agent({ | ||
| name: 'Дата агент сети доставок "Суши Love"', | ||
| instructions: 'У тебя есть доступ к данным партнеров сети. Отвечай всегда на русском в мужском роде.', | ||
| model: new OpenAIChatCompletionsModel(client, ai.model), | ||
| tools: [ | ||
| getPartnersTool, | ||
| getPartnersByCityTool, | ||
| ], | ||
| }) | ||
|
Comment on lines
+41
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add rate limiting and request timeout. The agent execution doesn't have rate limiting or timeout controls, which could lead to resource exhaustion or hanging requests. Consider adding timeout and basic rate limiting: const agent = new Agent({
name: 'Дата агент сети доставок "Суши Love"',
instructions: 'У тебя есть доступ к данным партнеров сети. Отвечай всегда на русском в мужском роде.',
model: new OpenAIChatCompletionsModel(client, ai.model),
tools: [
getPartnersTool,
getPartnersByCityTool,
],
+ maxToolCalls: 10, // Limit tool calls to prevent infinite loops
})Also consider implementing request timeout: -const result = await run(agent, body.message)
+const result = await Promise.race([
+ run(agent, message),
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error('Request timeout')), 30000)
+ )
+])
🤖 Prompt for AI Agents |
||
|
|
||
| const result = await run(agent, body.message) | ||
|
|
||
| return { | ||
| ok: true, | ||
| message: result.finalOutput, | ||
| } | ||
| } catch (error) { | ||
| throw errorResolver(error) | ||
| } | ||
| }) | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,27 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| import { tool } from '@openai/agents' | ||||||||||||||||||||||||||||||||||||||||||||
| import { repository } from '@roll-stack/database' | ||||||||||||||||||||||||||||||||||||||||||||
| import { z } from 'zod' | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| export const getPartnersTool = tool({ | ||||||||||||||||||||||||||||||||||||||||||||
| name: 'get_all_partners', | ||||||||||||||||||||||||||||||||||||||||||||
| description: 'Get all partners', | ||||||||||||||||||||||||||||||||||||||||||||
| needsApproval: false, | ||||||||||||||||||||||||||||||||||||||||||||
| parameters: z.object({}), | ||||||||||||||||||||||||||||||||||||||||||||
| execute: async () => { | ||||||||||||||||||||||||||||||||||||||||||||
| return repository.partner.list() | ||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+5
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider adding pagination and access control. The tool exposes all partner data without pagination or access control, which could lead to performance issues and potential data leaks. Consider adding pagination parameters and access control: export const getPartnersTool = tool({
name: 'get_all_partners',
- description: 'Get all partners',
+ description: 'Get paginated list of partners',
needsApproval: false,
- parameters: z.object({}),
- execute: async () => {
- return repository.partner.list()
+ parameters: z.object({
+ limit: z.number().min(1).max(100).optional().default(50),
+ offset: z.number().min(0).optional().default(0),
+ }),
+ execute: async ({ limit, offset }) => {
+ return repository.partner.list({ limit, offset })
},
})📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| export const getPartnersByCityTool = tool({ | ||||||||||||||||||||||||||||||||||||||||||||
| name: 'get_partners_by_city', | ||||||||||||||||||||||||||||||||||||||||||||
| description: 'Get partners filtered by city name using case-insensitive partial matching', | ||||||||||||||||||||||||||||||||||||||||||||
| needsApproval: false, | ||||||||||||||||||||||||||||||||||||||||||||
| parameters: z.object({ | ||||||||||||||||||||||||||||||||||||||||||||
| city: z.string(), | ||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||
| execute: async ({ city }) => { | ||||||||||||||||||||||||||||||||||||||||||||
| const partners = await repository.partner.list() | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| return partners.filter((partner) => partner.city?.toLowerCase().includes(city.toLowerCase())) | ||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add comprehensive input validation.
The current validation only checks for the presence of
messagebut doesn't validate its type, length, or content.Add proper input validation using zod:
Then use
messageinstead ofbody.messageon line 41.📝 Committable suggestion
🤖 Prompt for AI Agents