feat: ai agent#12
Conversation
WalkthroughThe changes introduce AI integration into the web application by adding configuration variables, dependencies, and backend logic for an OpenAI-powered agent endpoint. New tools for partner data retrieval are implemented, and related package versions are adjusted. Minor UI and configuration updates are also included. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API (agent/index.post.ts)
participant OpenAI Agent
participant Tools (tools.ts)
participant Repository
Client->>API (agent/index.post.ts): POST /api/agent with message & Authorization
API (agent/index.post.ts)->>API (agent/index.post.ts): Validate Authorization & message
API (agent/index.post.ts)->>OpenAI Agent: Initialize with config, tools, instructions
OpenAI Agent->>Tools (tools.ts): May call getPartnersTool or getPartnersByCityTool
Tools (tools.ts)->>Repository: Fetch partner data
Repository-->>Tools (tools.ts): Return partner data
Tools (tools.ts)-->>OpenAI Agent: Return tool result
OpenAI Agent-->>API (agent/index.post.ts): Return agent response
API (agent/index.post.ts)-->>Client: Respond with { ok, message }
Estimated code review effort3 (~40 minutes) Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (5)
.npmrc (1)
2-3: Potentially redundant hoisting flags
node-linker=hoistedalready gives you a fully-hoistednode_moduleslayout; keepingshamefully-hoist=trueon top of that rarely brings extra benefit and can slow down installs.
Unless you have a concrete case that still needsshamefully-hoist, consider dropping it to keep the config minimal.- shamefully-hoist=true node-linker=hoistedapps/web-app/.env.example (1)
17-22: Keep.env.examplekeys ordered & unquoted for linter parity
dotenv-linterflagged quoting + ordering.
To silence it (and match the rest of the file) you can drop the quotes and keep alphabetical order:-NUXT_AI_MODEL="" -NUXT_AI_BASE_URL="" -NUXT_AI_API_KEY="" -NUXT_AI_SERVICE_TOKEN="" +NUXT_AI_API_KEY= +NUXT_AI_BASE_URL= +NUXT_AI_MODEL= +NUXT_AI_SERVICE_TOKEN=apps/web-app/server/services/tools.ts (1)
17-17: Improve tool description for better AI understanding.The description mentions "requires rechecking" which could confuse the AI agent about the tool's reliability.
- description: 'Get partners by provided city name. The list returned may be longer than needed, requires rechecking', + description: 'Get partners filtered by city name using case-insensitive partial matching',apps/web-app/server/api/agent/index.post.ts (2)
47-49: Improve error handling specificity.The generic error resolver might not provide appropriate responses for AI-specific errors or external API failures.
Add specific error handling for common scenarios:
} catch (error) { + // Handle OpenAI-specific errors + if (error instanceof OpenAI.APIError) { + throw createError({ + statusCode: error.status || 500, + message: 'AI service error', + }) + } + + // Handle agent execution errors + if (error.message === 'Request timeout') { + throw createError({ + statusCode: 408, + message: 'Request timeout', + }) + } + throw errorResolver(error) }
32-33: Consider externalize agent configuration.Hard-coding the agent name and instructions in Russian makes the code less maintainable and internationalization-unfriendly.
Move these to configuration:
const agent = new Agent({ - name: 'Дата агент сети доставок "Суши Love"', - instructions: 'У тебя есть доступ к данным партнеров сети. Отвечай всегда на русском в мужском роде.', + name: ai.agentName || 'Дата агент сети доставок "Суши Love"', + instructions: ai.agentInstructions || 'У тебя есть доступ к данным партнеров сети. Отвечай всегда на русском в мужском роде.', model: new OpenAIChatCompletionsModel(client, ai.model),
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
.npmrc(1 hunks)apps/web-app/.env.example(1 hunks)apps/web-app/app/components/Navigation.vue(0 hunks)apps/web-app/nuxt.config.ts(1 hunks)apps/web-app/package.json(2 hunks)apps/web-app/server/api/agent/index.post.ts(1 hunks)apps/web-app/server/services/tools.ts(1 hunks)pnpm-workspace.yaml(3 hunks)
🧠 Learnings (1)
pnpm-workspace.yaml (1)
Learnt from: hmbanan666
PR: #11
File: docker/web-parser/Dockerfile:19-19
Timestamp: 2025-07-18T15:09:04.345Z
Learning: In Docker multi-stage builds with Playwright, the npx playwright install chromium --with-deps --only-shell command must be run in the production stage, not the builder stage, because --with-deps installs system-level dependencies (libraries, fonts, etc.) that need to be present in the runtime environment and cannot be copied between stages.
🪛 dotenv-linter (3.3.0)
apps/web-app/.env.example
[warning] 18-18: [QuoteCharacter] The value has quote characters (', ")
[warning] 19-19: [QuoteCharacter] The value has quote characters (', ")
[warning] 19-19: [UnorderedKey] The NUXT_AI_BASE_URL key should go before the NUXT_AI_MODEL key
[warning] 20-20: [QuoteCharacter] The value has quote characters (', ")
[warning] 20-20: [UnorderedKey] The NUXT_AI_API_KEY key should go before the NUXT_AI_BASE_URL key
[warning] 21-21: [QuoteCharacter] The value has quote characters (', ")
💤 Files with no reviewable changes (1)
- apps/web-app/app/components/Navigation.vue
🧰 Additional context used
🧠 Learnings (1)
pnpm-workspace.yaml (1)
Learnt from: hmbanan666
PR: #11
File: docker/web-parser/Dockerfile:19-19
Timestamp: 2025-07-18T15:09:04.345Z
Learning: In Docker multi-stage builds with Playwright, the npx playwright install chromium --with-deps --only-shell command must be run in the production stage, not the builder stage, because --with-deps installs system-level dependencies (libraries, fonts, etc.) that need to be present in the runtime environment and cannot be copied between stages.
🪛 dotenv-linter (3.3.0)
apps/web-app/.env.example
[warning] 18-18: [QuoteCharacter] The value has quote characters (', ")
[warning] 19-19: [QuoteCharacter] The value has quote characters (', ")
[warning] 19-19: [UnorderedKey] The NUXT_AI_BASE_URL key should go before the NUXT_AI_MODEL key
[warning] 20-20: [QuoteCharacter] The value has quote characters (', ")
[warning] 20-20: [UnorderedKey] The NUXT_AI_API_KEY key should go before the NUXT_AI_BASE_URL key
[warning] 21-21: [QuoteCharacter] The value has quote characters (', ")
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (4)
pnpm-workspace.yaml (2)
23-24: Cross-check OpenAI package versions for compatibility
@openai/agents@0.0.12was published againstopenai@5.8.x. Bumping the core SDK to5.10.1should work but hasn’t been tested by the Agents team yet.
Run the agent API endpoint in a dry-run and make sure tool execution still works (especially streaming).If you hit odd type errors or runtime 400s from OpenAI, try aligning both libs to the same minor.
Also applies to: 54-55
69-69: No v4-only Zod helpers detected; downgrade safe
I ran a comprehensive search forz.coerce,z.custom, anderrorMapacross all TS, TSX, JS, and JSX files and found no references. There are no v4-specific Zod APIs in use, so reverting tozod@3.25.67should not break existing code.apps/web-app/nuxt.config.ts (1)
15-20: Confirm secrets stay server-only
runtimeConfig.aiis private by default, which is what we want forapiKeyandserviceToken.
Just make sure you never spread the whole object intouseRuntimeConfig().publicin the future—leaking these two fields would expose credentials to the client.No change required, just a heads-up.
apps/web-app/package.json (1)
19-30: Missing types for@openai/agentsThat package ships only ESM JS; for TS projects you generally also need
@types/openai(included inopenai@5.x) but no types for the Agents wrapper.
If the IDE complains, consider adding atypesVersionworkaround or contributing upstream.Otherwise the additions look fine.
| const body = await readBody(event) | ||
| if (!body?.message) { | ||
| throw createError({ | ||
| statusCode: 400, | ||
| message: 'Message is required', | ||
| }) | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add comprehensive input validation.
The current validation only checks for the presence of message but doesn't validate its type, length, or content.
Add proper input validation using zod:
+import { z } from 'zod'
+const requestSchema = z.object({
+ message: z.string().min(1).max(1000).trim(),
+})
const body = await readBody(event)
-if (!body?.message) {
+
+const validation = requestSchema.safeParse(body)
+if (!validation.success) {
throw createError({
statusCode: 400,
- message: 'Message is required',
+ message: 'Invalid input: ' + validation.error.issues.map(i => i.message).join(', '),
})
}
+const { message } = validation.dataThen use message instead of body.message on line 41.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const body = await readBody(event) | |
| if (!body?.message) { | |
| throw createError({ | |
| statusCode: 400, | |
| message: 'Message is required', | |
| }) | |
| } | |
| import { z } from 'zod' | |
| const requestSchema = z.object({ | |
| message: z.string().min(1).max(1000).trim(), | |
| }) | |
| const body = await readBody(event) | |
| const validation = requestSchema.safeParse(body) | |
| if (!validation.success) { | |
| throw createError({ | |
| statusCode: 400, | |
| message: 'Invalid input: ' + validation.error.issues.map(i => i.message).join(', '), | |
| }) | |
| } | |
| const { message } = validation.data | |
| // …use `message` below instead of `body.message`… |
🤖 Prompt for AI Agents
In apps/web-app/server/api/agent/index.post.ts around lines 18 to 24, the
current input validation only checks if the message exists but does not validate
its type, length, or content. To fix this, define a zod schema that validates
the message field for type (string), minimum and maximum length, and any other
content rules needed. Use this schema to parse and validate the request body
instead of just checking presence. After validation, use the parsed message
variable instead of accessing body.message directly on line 41.
| const client = new OpenAI({ | ||
| apiKey: ai.apiKey, | ||
| baseURL: ai.baseUrl, | ||
| }) |
There was a problem hiding this comment.
🛠️ 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
In apps/web-app/server/api/agent/index.post.ts around lines 26 to 29, the OpenAI
client is instantiated without validating the presence of required configuration
values like apiKey and baseURL. Add validation checks before creating the client
to ensure ai.apiKey and ai.baseUrl are defined and valid. If any required
configuration is missing or invalid, throw an appropriate error or handle it
gracefully to prevent runtime errors.
| const agent = new Agent({ | ||
| name: 'Дата агент сети доставок "Суши Love"', | ||
| instructions: 'У тебя есть доступ к данным партнеров сети. Отвечай всегда на русском в мужском роде.', | ||
| model: new OpenAIChatCompletionsModel(client, ai.model), | ||
| tools: [ | ||
| getPartnersTool, | ||
| getPartnersByCityTool, | ||
| ], | ||
| }) |
There was a problem hiding this comment.
🛠️ 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)
+ )
+])Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web-app/server/api/agent/index.post.ts around lines 31 to 39, the Agent
instantiation lacks rate limiting and request timeout controls, which can cause
resource exhaustion or hanging requests. To fix this, implement a rate limiting
mechanism that restricts the number of requests per user or IP within a time
window before creating the Agent instance. Additionally, add a request timeout
feature that cancels or aborts the agent's execution if it exceeds a predefined
duration, ensuring the system remains responsive and stable.
| export const getPartnersTool = tool({ | ||
| name: 'get_all_partners', | ||
| description: 'Get all partners', | ||
| needsApproval: false, | ||
| parameters: z.object({}), | ||
| execute: async () => { | ||
| return repository.partner.list() | ||
| }, | ||
| }) |
There was a problem hiding this comment.
🛠️ 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
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const getPartnersTool = tool({ | |
| name: 'get_all_partners', | |
| description: 'Get all partners', | |
| needsApproval: false, | |
| parameters: z.object({}), | |
| execute: async () => { | |
| return repository.partner.list() | |
| }, | |
| }) | |
| export const getPartnersTool = tool({ | |
| name: 'get_all_partners', | |
| description: 'Get paginated list of partners', | |
| needsApproval: false, | |
| 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 }) | |
| }, | |
| }) |
🤖 Prompt for AI Agents
In apps/web-app/server/services/tools.ts around lines 5 to 13, the
getPartnersTool currently returns all partner data without pagination or access
control, risking performance and security issues. Modify the tool to accept
pagination parameters (like page number and page size) in its input schema, and
implement logic in the execute function to fetch only the requested page of
partners. Additionally, add access control checks to ensure only authorized
users can retrieve partner data.
| export const getPartnersByCityTool = tool({ | ||
| name: 'get_partners_by_city', | ||
| description: 'Get partners by provided city name. The list returned may be longer than needed, requires rechecking', | ||
| 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.
🛠️ Refactor suggestion
Optimize database query for city filtering.
The current implementation fetches all partners and then filters in memory, which is inefficient and doesn't scale well.
Move the filtering to the database layer:
export const getPartnersByCityTool = tool({
name: 'get_partners_by_city',
description: 'Get partners by provided city name. The list returned may be longer than needed, requires rechecking',
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()))
+ execute: async ({ city }) => {
+ return repository.partner.list({
+ where: {
+ city: {
+ contains: city,
+ mode: 'insensitive'
+ }
+ }
+ })
},
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const getPartnersByCityTool = tool({ | |
| name: 'get_partners_by_city', | |
| description: 'Get partners by provided city name. The list returned may be longer than needed, requires rechecking', | |
| 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())) | |
| }, | |
| }) | |
| export const getPartnersByCityTool = tool({ | |
| name: 'get_partners_by_city', | |
| description: 'Get partners by provided city name. The list returned may be longer than needed, requires rechecking', | |
| needsApproval: false, | |
| parameters: z.object({ | |
| city: z.string(), | |
| }), | |
| execute: async ({ city }) => { | |
| return repository.partner.list({ | |
| where: { | |
| city: { | |
| contains: city, | |
| mode: 'insensitive' | |
| } | |
| } | |
| }) | |
| }, | |
| }) |
🤖 Prompt for AI Agents
In apps/web-app/server/services/tools.ts around lines 15 to 27, the current code
fetches all partners from the database and filters them in memory by city, which
is inefficient. Modify the repository.partner.list() call or create a new method
to query the database directly with a filter on the city field using a
case-insensitive match. This moves the filtering logic to the database layer,
improving performance and scalability by returning only relevant partners.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
apps/web-app/.env.example (2)
8-12: Re-order the S3 keys to satisfy dotenv-linter and improve readability
dotenv-linterflags show that the current order is not alphabetical; this will keep failing CI if the linter runs.
A quick re-order fixes the issue.-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=
24-24: Add a trailing blank lineMany tooling setups (git, POSIX, editors) expect newline-terminated files; this also removes the
EndingBlankLinewarning.-VERSION= +VERSION= +
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
.npmrc(1 hunks)apps/web-app/.env.example(1 hunks)apps/web-app/server/api/agent/index.post.ts(1 hunks)apps/web-app/server/services/tools.ts(1 hunks)
🪛 dotenv-linter (3.3.0)
apps/web-app/.env.example
[warning] 10-10: [UnorderedKey] The NUXT_S3_ENDPOINT key should go before the NUXT_S3_REGION key
[warning] 11-11: [UnorderedKey] The NUXT_S3_ACCESS_KEY_ID key should go before the NUXT_S3_BUCKET key
[warning] 24-24: [EndingBlankLine] No blank line at the end of the file
🚧 Files skipped from review as they are similar to previous changes (3)
- .npmrc
- apps/web-app/server/services/tools.ts
- apps/web-app/server/api/agent/index.post.ts
🧰 Additional context used
🪛 dotenv-linter (3.3.0)
apps/web-app/.env.example
[warning] 10-10: [UnorderedKey] The NUXT_S3_ENDPOINT key should go before the NUXT_S3_REGION key
[warning] 11-11: [UnorderedKey] The NUXT_S3_ACCESS_KEY_ID key should go before the NUXT_S3_BUCKET key
[warning] 24-24: [EndingBlankLine] No blank line at the end of the file
🔇 Additional comments (1)
apps/web-app/.env.example (1)
17-21: Confirm visibility of new AI env varsIn Nuxt 3, variables without the
NUXT_PUBLIC_prefix are kept server-only.
That’s perfect forNUXT_AI_API_KEYandNUXT_AI_SERVICE_TOKEN(secrets), but double-check whetherNUXT_AI_MODELandNUXT_AI_BASE_URLneed to be available client-side.
If so, rename them with theNUXT_PUBLIC_prefix; otherwise keep as-is.
|



Summary by CodeRabbit
New Features
Improvements
Chores