Feat/ai orchestration ab testing#444
Conversation
|
@ZarrarPeshimam is attempting to deploy a commit to the Drago's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@Drago-03, plz review my PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a new Changes
Sequence DiagramsequenceDiagram
participant User
participant Frontend as Frontend UI
participant API as /api/groq
participant Orchestrator as Orchestration Layer
participant GroqService as Groq Service
participant Primary as Groq API (Primary)
participant Fallback as Groq API (Fallback)
participant Mongo as MongoDB
User->>Frontend: configure story & request generate
Frontend->>API: POST (prompt, title, genre, length, pipelineParams)
API->>Orchestrator: getOrchestrationPlan(type, preference)
Orchestrator-->>API: plan (primaryModel, fallbackModel?, variant, temperature)
API->>GroqService: generateStoryContent(params + plan)
GroqService->>Primary: call primary model
alt primary succeeds
Primary-->>GroqService: content
GroqService-->>API: {content, actualModel, fallbackUsed:false}
else primary fails
Primary-->>GroqService: error
GroqService->>Fallback: call fallback model
Fallback-->>GroqService: content
GroqService-->>API: {content, actualModel, fallbackUsed:true}
end
API->>Mongo: log generation event (prompt, model, variant, latency, status)
Mongo-->>API: logged
API-->>Frontend: response (content, model metadata)
Frontend->>Frontend: persist draft (DRAFT_KEY)
Frontend-->>User: display generated story
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
A merge conflict had occured |
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
lib/groq-service.ts (1)
82-101:⚠️ Potential issue | 🔴 CriticalAdd a request timeout so fallback can actually trigger on hangs.
Without a timeout, a stalled primary request may never throw, and fallback logic never runs.
🔧 Suggested fix
async function performRequest(targetModel: string): Promise<string> { @@ - const response = await fetch( - 'https://api.groq.com/openai/v1/chat/completions', - { - method: 'POST', - headers: { - Authorization: `Bearer ${groqApiKey}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - model: targetModel, - messages: [ - { role: 'system', content: systemPrompt }, - { role: 'user', content: wrapUserContent(prompt) }, - ], - max_tokens: getMaxTokensForLength(params.length || 'medium'), - temperature: selectedTemp, - top_p: 0.9, - }), - } - ); + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 15000); + let response: Response; + try { + response = await fetch( + 'https://api.groq.com/openai/v1/chat/completions', + { + method: 'POST', + headers: { + Authorization: `Bearer ${groqApiKey}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + model: targetModel, + messages: [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: wrapUserContent(prompt) }, + ], + max_tokens: getMaxTokensForLength(params.length || 'medium'), + temperature: selectedTemp, + top_p: 0.9, + }), + signal: controller.signal, + } + ); + } finally { + clearTimeout(timeoutId); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/groq-service.ts` around lines 82 - 101, The fetch call that posts to 'https://api.groq.com/openai/v1/chat/completions' (using groqApiKey, targetModel, wrapUserContent, getMaxTokensForLength, selectedTemp) needs a request timeout so the fallback path can run on hangs; wrap the fetch with an AbortController, start a timer (e.g., setTimeout) that calls controller.abort() after a reasonable timeout, pass controller.signal into fetch options, and clear the timer once fetch resolves or rejects so no stray timers remain; ensure the abort error is propagated so existing fallback logic sees the failure.lib/db/connect.ts (1)
72-115:⚠️ Potential issue | 🟠 MajorClose failed MongoDB clients inside the retry loop.
When a connect/ping attempt fails, the attempt-local client is not closed before retrying. Repeated failures can accumulate open sockets.
🔧 Suggested fix
connectionPromise = (async () => { for (let attempt = 1; attempt <= maxRetries; attempt++) { + let client: MongoClient | null = null; try { // 3. Log each specific attempt and the sanitized URI console.log(`[DB] Attempt ${attempt}/${maxRetries}: Connecting to ${sanitizedUri}`); state.connectionAttempts = attempt; - const client = new MongoClient(uri, { + client = new MongoClient(uri, { maxPoolSize: 10, serverSelectionTimeoutMS: 5000, socketTimeoutMS: 45000, }); await client.connect(); @@ console.log(`[DB] SUCCESS: Connected to database on attempt ${attempt}`); return client; } catch (error: any) { + if (client) { + await client.close().catch(() => {}); + } const errorMessage = error.message || 'Unknown error'; state.lastError = errorMessage; const delay = retryDelayMs * Math.pow(2, attempt - 1);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/db/connect.ts` around lines 72 - 115, The retry loop in the connect routine leaves the attempt-local MongoClient open on failures; inside the catch block of the for loop (where `client` is created via `new MongoClient(uri, { ... })` and `error` is handled), ensure you close the failed `client` before retrying (await client.close() safely, guarded in case client.connect() never completed), and also close it on the final max-retries failure before throwing; update the catch to attempt a safe close of the `client` and swallow/attach any close errors to `state.lastError` so sockets do not accumulate across attempts.
🧹 Nitpick comments (1)
lib/groq-service.ts (1)
134-147: Cache fallback successes to avoid repeated primary-fail-first behavior.Fallback output is returned but never cached, so identical requests can repeatedly incur primary failure overhead.
♻️ Suggested refactor
try { const fallbackContent = await performRequest(fallbackModel); const outputCheck = validateOutput(fallbackContent); if (!outputCheck.isSafe) throw new Error('Unsafe fallback output'); + const fallbackCacheOptions = { ...params, model: fallbackModel }; + await setCachedResponse(cacheCategory, params.theme, fallbackCacheOptions, fallbackContent); + return { content: fallbackContent, actualModel: fallbackModel, fallbackUsed: true };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/groq-service.ts` around lines 134 - 147, The fallback branch currently returns fallbackContent but does not store it in the cache, causing repeated primary failures; update the code around the fallback handling (the block using fallbackModel, performRequest, validateOutput and returning { content, actualModel, fallbackUsed }) to save the successful fallback response into the same caching mechanism used for primary responses (create a cache key derived from the request parameters, store content, actualModel and fallbackUsed=true with TTL), and ensure the request flow checks that cache before attempting the primary model so identical requests use the cached fallback result instead of reattempting the primary; use the existing cache helper (or add one) and keep validation via validateOutput before caching.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/api/groq/route.ts`:
- Around line 70-77: The telemetry insertion currently writes a raw/truncated
promptSnippet into the generation_logs record; change this to avoid storing any
raw prompt text by replacing promptSnippet with a non-reversible identifier and
metadata (e.g., compute and store a secure hash of prompt using a SHA-256 or
similar and store promptLength) in the
db.collection('generation_logs').insertOne call so the record contains
promptHash and promptLength instead of promptSnippet; ensure generationResult
and plan usage is unchanged and update any downstream consumers to use
promptHash/promptLength rather than promptSnippet.
- Around line 141-147: The test branch handling "action === 'test'" in the
request handler (where searchParams and useSpecialModel decide between
testGroqSpecialModel and testGroqConnection and return via NextResponse.json) is
exposed to unauthenticated callers; add an internal guard at the top of that
branch that verifies a server-only secret or internal header (e.g., compare a
request header like "x-internal-token" against process.env.INTERNAL_API_KEY or
validate a server session) and if the check fails return a 403/NextResponse.json
error immediately; only call testGroqSpecialModel or testGroqConnection after
the guard passes so external requests cannot trigger provider calls.
- Around line 41-43: The current truthy check for prompt allows non-string or
whitespace values; change the validation inside the request handler to ensure
prompt is a string with non-empty trimmed content (e.g., check typeof prompt ===
'string' && prompt.trim().length > 0) and if it fails return NextResponse.json({
error: 'Prompt must be a non-empty string' }, { status: 400 }); update the block
that currently uses if (!prompt) to use this stricter check so downstream
logging and generation (references to prompt) always receive a valid string.
In `@components/ai-story-generator.tsx`:
- Line 690: The external OpenSea anchor uses target="_blank" without safe link
attributes; update the JSX where mintedNftUrl is rendered (the Button/asChild
anchor with target="_blank") to add rel="noopener noreferrer" to the <a> element
so the anchor rendering mintedNftUrl prevents opener/tab access and avoids the
security vulnerability.
In `@config/experiments.json`:
- Around line 3-4: The configured primary_story_model "llama-3.3-6767" is not
present in GROQ_MODELS in lib/groq-service.ts, causing the primary path to
always fail; update the experiments.json primary_story_model to one of the valid
IDs listed in GROQ_MODELS (e.g., "llama-3.3-70b-versatile",
"llama-3.1-8b-instant", or "mistral-saba-24b") or add the missing model key to
GROQ_MODELS in lib/groq-service.ts so the primary_story_model resolves
correctly, ensuring the symbol primary_story_model matches an entry in
GROQ_MODELS.
In `@lib/ai/orchestrator.ts`:
- Around line 19-37: The returned plan always sets fallbackModel, ignoring the
experiment toggle; update the orchestration logic (where config =
experiments.active_experiments and the plan objects are built) to respect
config.enable_fallback_logic: only include fallbackModel (or set it to
null/undefined) when config.enable_fallback_logic is true, and omit it when
false for both the speed/flash branch and the balanced/high-quality branch
(adjust temperature/promptVariant behavior unchanged).
In `@lib/db/connect.ts`:
- Around line 247-253: getDb currently uses a non-null assertion on
process.env.MONGODB_URI and calls client.db() without a database name; change it
to fail fast if MONGODB_URI is missing and explicitly pass the DB name when
calling client.db. Specifically, in the getDb function (which calls
connectWithRetry), validate process.env.MONGODB_URI and
process.env.MONGODB_DB_NAME up front and throw a clear error if either is
missing, then call client.db(process.env.MONGODB_DB_NAME) instead of client.db()
so the connection always targets the configured database.
In `@lib/groq-service.ts`:
- Around line 68-70: Add validation for model IDs before making any API calls:
in lib/groq-service.ts (where primaryModel, fallbackModel and selectedTemp are
selected) verify that primaryModel and fallbackModel (if provided) are in the
allowed model set (or match the provider's canonical ID pattern) and throw or
return a clear configuration error if not; perform this check immediately after
the lines assigning primaryModel/fallbackModel to fail fast on typos, and
include the invalid value in the error message. Also update
config/experiments.json to correct primary_story_model from "llama-3.3-6767" to
"llama-3.3-70b-versatile".
In `@models/GenerationLog.ts`:
- Around line 5-17: The interface IGenerationLog currently marks genre as
required while GenerationLogSchema defines genre as optional; update
IGenerationLog.genre to be optional (e.g., genre?: string) so the TypeScript
type matches GenerationLogSchema and consumers don't assume genre is always
present; locate the IGenerationLog declaration and change the genre property
accordingly to keep modelUsed, prompt, fallbackTriggered, variantId, latencyMs,
status, and timestamp unchanged.
- Around line 14-30: The GenerationLogSchema currently lacks an explicit
collection binding so mongoose.model('GenerationLog', GenerationLogSchema) will
default to the pluralized "generationlogs" collection; update the Schema
instantiation for GenerationLogSchema to include the collection option matching
the route ("generation_logs") by passing { collection: 'generation_logs' } into
the new Schema(...) call so the schema and the route write to the same
collection.
---
Outside diff comments:
In `@lib/db/connect.ts`:
- Around line 72-115: The retry loop in the connect routine leaves the
attempt-local MongoClient open on failures; inside the catch block of the for
loop (where `client` is created via `new MongoClient(uri, { ... })` and `error`
is handled), ensure you close the failed `client` before retrying (await
client.close() safely, guarded in case client.connect() never completed), and
also close it on the final max-retries failure before throwing; update the catch
to attempt a safe close of the `client` and swallow/attach any close errors to
`state.lastError` so sockets do not accumulate across attempts.
In `@lib/groq-service.ts`:
- Around line 82-101: The fetch call that posts to
'https://api.groq.com/openai/v1/chat/completions' (using groqApiKey,
targetModel, wrapUserContent, getMaxTokensForLength, selectedTemp) needs a
request timeout so the fallback path can run on hangs; wrap the fetch with an
AbortController, start a timer (e.g., setTimeout) that calls controller.abort()
after a reasonable timeout, pass controller.signal into fetch options, and clear
the timer once fetch resolves or rejects so no stray timers remain; ensure the
abort error is propagated so existing fallback logic sees the failure.
---
Nitpick comments:
In `@lib/groq-service.ts`:
- Around line 134-147: The fallback branch currently returns fallbackContent but
does not store it in the cache, causing repeated primary failures; update the
code around the fallback handling (the block using fallbackModel,
performRequest, validateOutput and returning { content, actualModel,
fallbackUsed }) to save the successful fallback response into the same caching
mechanism used for primary responses (create a cache key derived from the
request parameters, store content, actualModel and fallbackUsed=true with TTL),
and ensure the request flow checks that cache before attempting the primary
model so identical requests use the cached fallback result instead of
reattempting the primary; use the existing cache helper (or add one) and keep
validation via validateOutput before caching.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 71a8817f-e481-4862-b3c7-462d7b7fe40c
📒 Files selected for processing (7)
app/api/groq/route.tscomponents/ai-story-generator.tsxconfig/experiments.jsonlib/ai/orchestrator.tslib/db/connect.tslib/groq-service.tsmodels/GenerationLog.ts
| const primaryModel = params.model || GROQ_MODELS.STORY_GENERATION; | ||
| const fallbackModel = params.fallbackModel; | ||
| const selectedTemp = params.temperature ?? 0.8; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, check if the files mentioned exist
git ls-files | grep -E "(groq-service|experiments\.json)" | head -20Repository: IndieHub25/GroqTales
Length of output: 142
🏁 Script executed:
# Check the structure of lib/groq-service.ts around lines 68-70
wc -l lib/groq-service.tsRepository: IndieHub25/GroqTales
Length of output: 87
🏁 Script executed:
# Read the relevant section of lib/groq-service.ts
sed -n '60,85p' lib/groq-service.tsRepository: IndieHub25/GroqTales
Length of output: 1069
🏁 Script executed:
# Find and display GROQ_MODELS definition
rg "GROQ_MODELS\s*=" lib/groq-service.ts -A 15Repository: IndieHub25/GroqTales
Length of output: 628
🏁 Script executed:
# Check the experiments.json file to see configured models
cat config/experiments.json | jq '.active_experiments | {primary_story_model, fallback_story_model}'Repository: IndieHub25/GroqTales
Length of output: 159
🏁 Script executed:
# Check if there's any validation logic after line 70
sed -n '70,150p' lib/groq-service.ts | head -40Repository: IndieHub25/GroqTales
Length of output: 1551
🏁 Script executed:
# Search for any existing validation of model IDs in the file
rg "allowedModels|supported.*model|GROQ_MODELS.*has|valid.*model" lib/groq-service.ts -iRepository: IndieHub25/GroqTales
Length of output: 46
🏁 Script executed:
# Test the Python script from the review comment
python3 - <<'PY'
import json, re, pathlib
exp = json.loads(pathlib.Path("config/experiments.json").read_text())
ts = pathlib.Path("lib/groq-service.ts").read_text()
block = ts.split("export const GROQ_MODELS = {", 1)[1].split("} as const;", 1)[0]
valid = set(re.findall(r":\s*'([^']+)'", block))
print("Valid models:", valid)
print("\nActive experiments:")
for k in ("primary_story_model", "fallback_story_model"):
v = exp["active_experiments"].get(k)
if v:
status = 'OK' if v in valid else 'INVALID'
print(f" {k}: {v} -> {status}")
PYRepository: IndieHub25/GroqTales
Length of output: 270
Add model ID validation before API calls to catch misconfiguration immediately.
This function accepts any model string without validation. Verification found that config/experiments.json contains an invalid model ID (llama-3.3-6767 should be llama-3.3-70b-versatile), which will cause a runtime API failure. Without validation, typos in model configuration become avoidable provider failures.
🔧 Suggested fix
export async function generateStoryContent(
params: StoryGenerationParams & { fallbackModel?: string }
): Promise<{ content: string; actualModel: string; fallbackUsed: boolean }> {
const primaryModel = params.model || GROQ_MODELS.STORY_GENERATION;
const fallbackModel = params.fallbackModel;
const selectedTemp = params.temperature ?? 0.8;
+ const allowedModels = new Set(Object.values(GROQ_MODELS));
+
+ if (!allowedModels.has(primaryModel)) {
+ throw new Error(`Unsupported primary model: ${primaryModel}`);
+ }
+ if (fallbackModel && !allowedModels.has(fallbackModel)) {
+ throw new Error(`Unsupported fallback model: ${fallbackModel}`);
+ }Also fix the invalid model ID in config/experiments.json: change primary_story_model from llama-3.3-6767 to llama-3.3-70b-versatile.
📝 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 primaryModel = params.model || GROQ_MODELS.STORY_GENERATION; | |
| const fallbackModel = params.fallbackModel; | |
| const selectedTemp = params.temperature ?? 0.8; | |
| export async function generateStoryContent( | |
| params: StoryGenerationParams & { fallbackModel?: string } | |
| ): Promise<{ content: string; actualModel: string; fallbackUsed: boolean }> { | |
| const primaryModel = params.model || GROQ_MODELS.STORY_GENERATION; | |
| const fallbackModel = params.fallbackModel; | |
| const selectedTemp = params.temperature ?? 0.8; | |
| const allowedModels = new Set(Object.values(GROQ_MODELS)); | |
| if (!allowedModels.has(primaryModel)) { | |
| throw new Error(`Unsupported primary model: ${primaryModel}`); | |
| } | |
| if (fallbackModel && !allowedModels.has(fallbackModel)) { | |
| throw new Error(`Unsupported fallback model: ${fallbackModel}`); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/groq-service.ts` around lines 68 - 70, Add validation for model IDs
before making any API calls: in lib/groq-service.ts (where primaryModel,
fallbackModel and selectedTemp are selected) verify that primaryModel and
fallbackModel (if provided) are in the allowed model set (or match the
provider's canonical ID pattern) and throw or return a clear configuration error
if not; perform this check immediately after the lines assigning
primaryModel/fallbackModel to fail fast on typos, and include the invalid value
in the error message. Also update config/experiments.json to correct
primary_story_model from "llama-3.3-6767" to "llama-3.3-70b-versatile".
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
Drago-03
left a comment
There was a problem hiding this comment.
Update with the requested changes
| @@ -0,0 +1,12 @@ | |||
| { | |||
| "active_experiments": { | |||
| "primary_story_model": "llama-3.3-6767", | |||
There was a problem hiding this comment.
update primary_story_model to gemini-3-flash-preview (and fallback_story_model to `gemini-3.1-flash-lite-preview)
| maxRetries: 5, | ||
| retryDelayMs: 1000 | ||
| }); | ||
| return client.db(); // Returns the native Db object |
There was a problem hiding this comment.
-
Read
MONGODB_URIinto a local uri. -
If it’s missing, throw
new Error('MONGODB_URI is not configured'). -
Then call
connectWithRetry({ uri, ... }). -
Finally, return
client.db(process.env.MONGODB_DB_NAME)so the DB is always explicit.
| const primaryModel = params.model || GROQ_MODELS.STORY_GENERATION; | ||
| const fallbackModel = params.fallbackModel; | ||
| const selectedTemp = params.temperature ?? 0.8; |
| }); | ||
|
|
||
| export default mongoose.models.GenerationLog || | ||
| mongoose.model<IGenerationLog>('GenerationLog', GenerationLogSchema); No newline at end of file |
There was a problem hiding this comment.
add the collection bounding here
|
Done with required changes |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
lib/groq-service.ts (2)
159-162: Error message may be misleading.The message "All available AI models are currently overloaded" assumes rate limiting, but failures could stem from authentication errors, invalid requests, or network issues. Consider a more accurate message.
♻️ Suggested improvement
} catch (fallbackError) { console.error('Both primary and fallback models failed.'); - throw new Error('All available AI models are currently overloaded.'); + throw new Error('Story generation failed: both primary and fallback models encountered errors.'); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/groq-service.ts` around lines 159 - 162, The catch block that currently logs "Both primary and fallback models failed." and throws a new Error('All available AI models are currently overloaded.') should be updated to avoid assuming rate-limiting: include the underlying fallbackError details in the thrown error or replace the static message with a generic one such as 'AI model request failed' and append or wrap fallbackError to preserve original error information; locate the try/catch around the primary/fallback model calls (the catch handling variable fallbackError in groq-service.ts) and modify the throw to include fallbackError.message or rethrow a new Error that composes a clear, non-assuming message plus the original error details.
29-31: RedundantfallbackModeldeclaration.
fallbackModelis already declared inStoryGenerationParams(line 30), so the intersection& { fallbackModel?: string }in the function signature (line 66) is redundant.♻️ Suggested simplification
export async function generateStoryContent( - params: StoryGenerationParams & { fallbackModel?: string } + params: StoryGenerationParams ): Promise<{ content: string; actualModel: string; fallbackUsed: boolean }> {Also applies to: 66-66
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/groq-service.ts` around lines 29 - 31, Remove the redundant fallbackModel declaration in the function signature that intersects StoryGenerationParams with "& { fallbackModel?: string }"; update the relevant function (the one accepting StoryGenerationParams) to accept StoryGenerationParams directly (or the existing parameter type) and delete the extra "& { fallbackModel?: string }" intersection so there's only a single fallbackModel declaration coming from StoryGenerationParams (refer to StoryGenerationParams and the function signature where "& { fallbackModel?: string }" appears).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/groq-service.ts`:
- Around line 119-121: The function currently returns the literal fallback
'Failed to generate story content' when data.choices[0]?.message?.content is
undefined, which may be cached as valid output; change the behavior in the block
that reads response.json() and inspects data.choices so that instead of
returning that string you either throw a descriptive error (including response
status/body) or return null/undefined so the caller can detect failure and avoid
caching; reference the variables/expressions data, response.json(), and
data.choices[0]?.message?.content and ensure any thrown error includes context
so upstream cache logic can skip storing invalid results.
- Around line 87-91: The code only sanitizes params.theme before calling
buildStoryPrompt; sanitize all user-supplied inputs used in that prompt
(params.genre, params.tone, params.characters, params.setting and theme) using
the existing sanitizeInput utility and pass the sanitized values into
buildStoryPrompt instead of raw params; update the call site where
sanitizedTheme is created (and any local variable names) so buildStoryPrompt
receives an object composed of sanitizedGenre, sanitizedTone,
sanitizedCharacters, sanitizedSetting, and sanitizedTheme while leaving
buildHardenedSystemPrompt unchanged.
---
Nitpick comments:
In `@lib/groq-service.ts`:
- Around line 159-162: The catch block that currently logs "Both primary and
fallback models failed." and throws a new Error('All available AI models are
currently overloaded.') should be updated to avoid assuming rate-limiting:
include the underlying fallbackError details in the thrown error or replace the
static message with a generic one such as 'AI model request failed' and append
or wrap fallbackError to preserve original error information; locate the
try/catch around the primary/fallback model calls (the catch handling variable
fallbackError in groq-service.ts) and modify the throw to include
fallbackError.message or rethrow a new Error that composes a clear, non-assuming
message plus the original error details.
- Around line 29-31: Remove the redundant fallbackModel declaration in the
function signature that intersects StoryGenerationParams with "& {
fallbackModel?: string }"; update the relevant function (the one accepting
StoryGenerationParams) to accept StoryGenerationParams directly (or the existing
parameter type) and delete the extra "& { fallbackModel?: string }" intersection
so there's only a single fallbackModel declaration coming from
StoryGenerationParams (refer to StoryGenerationParams and the function signature
where "& { fallbackModel?: string }" appears).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7df90872-93fc-4547-a05e-8db4f89266e2
📒 Files selected for processing (5)
config/experiments.jsonlib/ai/orchestrator.tslib/db/connect.tslib/groq-service.tsmodels/GenerationLog.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- models/GenerationLog.ts
- lib/ai/orchestrator.ts
- lib/db/connect.ts
- config/experiments.json
| const sanitizedTheme = sanitizeInput(params.theme).sanitized; | ||
| const prompt = buildStoryPrompt({ ...params, theme: sanitizedTheme }); | ||
| const systemPrompt = buildHardenedSystemPrompt( | ||
| 'You are a creative writing assistant that generates engaging, well-structured stories based on user parameters. Focus on compelling narratives with strong character development and vivid descriptions.' | ||
| 'You are a creative writing assistant that generates engaging, well-structured stories.' | ||
| ); |
There was a problem hiding this comment.
Inconsistent input sanitization.
Only params.theme is sanitized before prompt construction (line 87), while params.genre, params.tone, params.characters, and params.setting are used unsanitized in buildStoryPrompt. Other functions in this file (e.g., analyzeStoryContent, improveStoryContent) sanitize all user inputs. Apply consistent sanitization here.
🛡️ Suggested fix
async function performRequest(targetModel: string): Promise<string> {
const groqApiKey = process.env.GROQ_API_KEY;
if (!groqApiKey) throw new Error('GROQ_API_KEY is not set');
const sanitizedTheme = sanitizeInput(params.theme).sanitized;
- const prompt = buildStoryPrompt({ ...params, theme: sanitizedTheme });
+ const sanitizedParams = {
+ ...params,
+ theme: sanitizedTheme,
+ genre: params.genre ? sanitizeInput(params.genre).sanitized : undefined,
+ tone: params.tone ? sanitizeInput(params.tone).sanitized : undefined,
+ characters: params.characters ? sanitizeInput(params.characters).sanitized : undefined,
+ setting: params.setting ? sanitizeInput(params.setting).sanitized : undefined,
+ };
+ const prompt = buildStoryPrompt(sanitizedParams);📝 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 sanitizedTheme = sanitizeInput(params.theme).sanitized; | |
| const prompt = buildStoryPrompt({ ...params, theme: sanitizedTheme }); | |
| const systemPrompt = buildHardenedSystemPrompt( | |
| 'You are a creative writing assistant that generates engaging, well-structured stories based on user parameters. Focus on compelling narratives with strong character development and vivid descriptions.' | |
| 'You are a creative writing assistant that generates engaging, well-structured stories.' | |
| ); | |
| const sanitizedTheme = sanitizeInput(params.theme).sanitized; | |
| const sanitizedParams = { | |
| ...params, | |
| theme: sanitizedTheme, | |
| genre: params.genre ? sanitizeInput(params.genre).sanitized : undefined, | |
| tone: params.tone ? sanitizeInput(params.tone).sanitized : undefined, | |
| characters: params.characters ? sanitizeInput(params.characters).sanitized : undefined, | |
| setting: params.setting ? sanitizeInput(params.setting).sanitized : undefined, | |
| }; | |
| const prompt = buildStoryPrompt(sanitizedParams); | |
| const systemPrompt = buildHardenedSystemPrompt( | |
| 'You are a creative writing assistant that generates engaging, well-structured stories.' | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/groq-service.ts` around lines 87 - 91, The code only sanitizes
params.theme before calling buildStoryPrompt; sanitize all user-supplied inputs
used in that prompt (params.genre, params.tone, params.characters,
params.setting and theme) using the existing sanitizeInput utility and pass the
sanitized values into buildStoryPrompt instead of raw params; update the call
site where sanitizedTheme is created (and any local variable names) so
buildStoryPrompt receives an object composed of sanitizedGenre, sanitizedTone,
sanitizedCharacters, sanitizedSetting, and sanitizedTheme while leaving
buildHardenedSystemPrompt unchanged.
| const data = await response.json(); | ||
| const generatedContent = | ||
| data.choices[0]?.message?.content || 'Failed to generate story content'; | ||
| return data.choices[0]?.message?.content || 'Failed to generate story content'; | ||
| } |
There was a problem hiding this comment.
Silent failure returns a string that may be cached as valid content.
If data.choices[0]?.message?.content is undefined, the function returns 'Failed to generate story content' which will pass output validation and get cached. Subsequent requests with the same cache key will receive this error string as valid content.
🐛 Proposed fix
const data = await response.json();
- return data.choices[0]?.message?.content || 'Failed to generate story content';
+ const content = data.choices[0]?.message?.content;
+ if (!content) {
+ throw new Error('Groq API returned empty content');
+ }
+ return content;
}📝 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 data = await response.json(); | |
| const generatedContent = | |
| data.choices[0]?.message?.content || 'Failed to generate story content'; | |
| return data.choices[0]?.message?.content || 'Failed to generate story content'; | |
| } | |
| const data = await response.json(); | |
| const content = data.choices[0]?.message?.content; | |
| if (!content) { | |
| throw new Error('Groq API returned empty content'); | |
| } | |
| return content; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/groq-service.ts` around lines 119 - 121, The function currently returns
the literal fallback 'Failed to generate story content' when
data.choices[0]?.message?.content is undefined, which may be cached as valid
output; change the behavior in the block that reads response.json() and inspects
data.choices so that instead of returning that string you either throw a
descriptive error (including response status/body) or return null/undefined so
the caller can detect failure and avoid caching; reference the
variables/expressions data, response.json(), and
data.choices[0]?.message?.content and ensure any thrown error includes context
so upstream cache logic can skip storing invalid results.
|
Hi @Drago-03, I'd pushed the requested fixes that you had requested Please let me know if anything else needs. |
|
Had a merged conflict, resolved it |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/ai-story-generator.tsx`:
- Around line 92-111: The draft recovery state is set on mount (recoveredDraft,
showRecoveryModal) but there is no UI or handlers to resume or dismiss it; add a
visible recovery modal/confirmation component that renders when
showRecoveryModal is true and implement two handlers (e.g., handleResumeDraft
and handleDismissDraft) that respectively hydrate the editor/form state from
recoveredDraft (call whatever form setters you have, e.g., setPrompt, setTitle
or setFormFromDraft) and clear the saved draft
(localStorage.removeItem(DRAFT_KEY)) while closing the modal
(setShowRecoveryModal(false)); also ensure dismissing the modal does not
re-trigger recovery on next mount by removing the draft or setting a session
lock (mintSessionLock) as appropriate.
- Around line 247-254: The "MINT MASTERPIECE" button rendered when connected has
no onClick, so the mint CTA is dead; either wire it to the mint handler (e.g.,
call an existing mint function such as handleMint or mintMasterpiece from the
component) by adding onClick={handleMint} to the Button, or explicitly render a
disabled/placeholder state (disabled prop and label like "COMING SOON" and
appropriate styling) until minting is implemented; ensure you reference the same
state/props used for connectWallet and connected so the behavior is consistent.
- Around line 133-140: The client is sending generation params nested under
options (genres, length, temp) but the API (/api/groq route handler and
lib/groq-service.ts) expects flattened fields (top-level genre, length,
temp/temperature) and also expects options.preference plus mainCharacterName and
characterTraits to be present; update the payload in
components/ai-story-generator.tsx (the fetch POST for action 'generate') to send
flattened keys (e.g., genre, length, temp/temperature) at the top level, include
options.preference as its own field or as top-level preference, and include
mainCharacterName and characterTraits so the route handler and
lib/groq-service.ts consume them correctly, or alternatively update the
/api/groq route and lib/groq-service.ts to accept the current nested shape—pick
one and make all three places (client payload, route.ts handler,
lib/groq-service.ts) consistent.
- Around line 164-169: resetForm currently only clears prompt and
generatedStory, leaving other draft state to persist; update resetForm to also
reset storyTitle, mainCharacterName, selectedGenres, storyLength,
characterTraits, and temperature (and any other draft-related state vars) back
to their initial defaults, keep setActiveTab('input') and
localStorage.removeItem(DRAFT_KEY), and ensure any handlers that autosave read
the cleared values so the draft is not re-saved with stale data.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9e1bc2c4-b5f2-4809-841b-696f10499ab3
📒 Files selected for processing (1)
components/ai-story-generator.tsx
| const [showRecoveryModal, setShowRecoveryModal] = useState(false); | ||
| const [recoveredDraft, setRecoveredDraft] = useState<StoryDraft | null>(null); | ||
|
|
||
| const { toast } = useToast(); | ||
| const mintSessionLock = useRef(false); | ||
|
|
||
| const handleGenreToggle = (genre: string) => { | ||
| setSelectedGenres((prev) => | ||
| prev.includes(genre) ? prev.filter((g) => g !== genre) : [...prev, genre] | ||
| ); | ||
| }; | ||
|
|
||
| const generateStory = async () => { | ||
| if (!prompt.trim()) { | ||
| toast({ | ||
| title: 'Missing Prompt', | ||
| description: 'Please enter a story prompt to generate content.', | ||
| variant: 'destructive', | ||
| }); | ||
| return; | ||
| // Draft Recovery Logic | ||
| useEffect(() => { | ||
| const saved = localStorage.getItem(DRAFT_KEY); | ||
| if (saved) { | ||
| try { | ||
| const draft = JSON.parse(saved); | ||
| if (draft?.prompt?.trim()) { | ||
| setRecoveredDraft(draft); | ||
| setShowRecoveryModal(true); | ||
| } | ||
| } catch (e) { | ||
| localStorage.removeItem(DRAFT_KEY); | ||
| } | ||
| } | ||
| }, []); |
There was a problem hiding this comment.
Wire the recovery flow before shipping.
recoveredDraft and showRecoveryModal are populated on mount, but nothing in this file ever renders a recovery UI or hydrates the form from that state. Saved drafts can be detected, but they can never be resumed or dismissed, so the same local draft will keep being rediscovered on every mount.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/ai-story-generator.tsx` around lines 92 - 111, The draft recovery
state is set on mount (recoveredDraft, showRecoveryModal) but there is no UI or
handlers to resume or dismiss it; add a visible recovery modal/confirmation
component that renders when showRecoveryModal is true and implement two handlers
(e.g., handleResumeDraft and handleDismissDraft) that respectively hydrate the
editor/form state from recoveredDraft (call whatever form setters you have,
e.g., setPrompt, setTitle or setFormFromDraft) and clear the saved draft
(localStorage.removeItem(DRAFT_KEY)) while closing the modal
(setShowRecoveryModal(false)); also ensure dismissing the modal does not
re-trigger recovery on next mount by removing the draft or setting a session
lock (mintSessionLock) as appropriate.
| const response = await fetch('/api/groq', { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ | ||
| action: 'generate', | ||
| prompt: prompt.trim(), | ||
| title: title || undefined, | ||
| genre: selectedGenres.join(', ') || undefined, | ||
| setting: setting || undefined, | ||
| characters: mainCharacters || undefined, | ||
| themes: themes || undefined, | ||
| format: 'short', | ||
| maxTokens: 2000, | ||
| prompt, | ||
| options: { title: storyTitle, genres: selectedGenres, length: storyLength, temp: temperature[0] } | ||
| }), |
There was a problem hiding this comment.
Align this payload with /api/groq before relying on the new orchestration path.
app/api/groq/route.ts:22-61 reads top-level genre / length and only checks options.preference, while lib/groq-service.ts:65-71 consumes flattened generation params. Sending genres, length, and temp inside options means the selected genre, story length, and temperature are ignored, options.preference is never sent, and mainCharacterName / characterTraits never leave the client. Either flatten the payload here or update the route to consume this shape.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/ai-story-generator.tsx` around lines 133 - 140, The client is
sending generation params nested under options (genres, length, temp) but the
API (/api/groq route handler and lib/groq-service.ts) expects flattened fields
(top-level genre, length, temp/temperature) and also expects options.preference
plus mainCharacterName and characterTraits to be present; update the payload in
components/ai-story-generator.tsx (the fetch POST for action 'generate') to send
flattened keys (e.g., genre, length, temp/temperature) at the top level, include
options.preference as its own field or as top-level preference, and include
mainCharacterName and characterTraits so the route handler and
lib/groq-service.ts consume them correctly, or alternatively update the
/api/groq route and lib/groq-service.ts to accept the current nested shape—pick
one and make all three places (client payload, route.ts handler,
lib/groq-service.ts) consistent.
| const resetForm = () => { | ||
| setPrompt(''); | ||
| setTitle(''); | ||
| setMainCharacters(''); | ||
| setSetting(''); | ||
| setThemes(''); | ||
| setSelectedGenres([]); | ||
| setGeneratedContent(''); | ||
| setGeneratedStory(null); | ||
| setActiveTab('input'); | ||
| localStorage.removeItem(DRAFT_KEY); | ||
| }; |
There was a problem hiding this comment.
START OVER should clear the whole draft state.
This only resets prompt and generatedStory. storyTitle, mainCharacterName, selectedGenres, storyLength, characterTraits, and temperature all survive, so the next run starts with stale hidden state and those values get autosaved again.
🔧 Suggested fix
const resetForm = () => {
setPrompt('');
+ setStoryTitle('');
+ setSelectedGenres([]);
+ setStoryLength('medium');
+ setMainCharacterName('');
+ setCharacterTraits([]);
+ setTemperature([0.7]);
setGeneratedStory(null);
setActiveTab('input');
localStorage.removeItem(DRAFT_KEY);
};📝 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 resetForm = () => { | |
| setPrompt(''); | |
| setTitle(''); | |
| setMainCharacters(''); | |
| setSetting(''); | |
| setThemes(''); | |
| setSelectedGenres([]); | |
| setGeneratedContent(''); | |
| setGeneratedStory(null); | |
| setActiveTab('input'); | |
| localStorage.removeItem(DRAFT_KEY); | |
| }; | |
| const resetForm = () => { | |
| setPrompt(''); | |
| setStoryTitle(''); | |
| setSelectedGenres([]); | |
| setStoryLength('medium'); | |
| setMainCharacterName(''); | |
| setCharacterTraits([]); | |
| setTemperature([0.7]); | |
| setGeneratedStory(null); | |
| setActiveTab('input'); | |
| localStorage.removeItem(DRAFT_KEY); | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/ai-story-generator.tsx` around lines 164 - 169, resetForm
currently only clears prompt and generatedStory, leaving other draft state to
persist; update resetForm to also reset storyTitle, mainCharacterName,
selectedGenres, storyLength, characterTraits, and temperature (and any other
draft-related state vars) back to their initial defaults, keep
setActiveTab('input') and localStorage.removeItem(DRAFT_KEY), and ensure any
handlers that autosave read the cleared values so the draft is not re-saved with
stale data.
| <TabsContent value="mint" className="text-center py-10"> | ||
| <Wallet className="h-20 w-20 mx-auto mb-6" /> | ||
| <h3 className="font-bangers text-4xl mb-6">MINT AS NFT?</h3> | ||
| {!connected ? ( | ||
| <Button onClick={connectWallet} className="bg-black text-white font-bangers text-xl px-10 py-4 border-4 border-black">CONNECT WALLET</Button> | ||
| ) : ( | ||
| <Button className="bg-purple-500 text-white font-bangers text-2xl px-10 py-6 border-4 border-black shadow-[6px_6px_0px_0px_rgba(0,0,0,1)]">MINT MASTERPIECE</Button> | ||
| )} |
There was a problem hiding this comment.
Don't ship a dead mint CTA.
When connected is true, the primary action at Line 253 has no onClick, so step 3 of the flow does nothing. Please either wire the mint handler here or render a disabled “coming soon” state until minting is implemented.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/ai-story-generator.tsx` around lines 247 - 254, The "MINT
MASTERPIECE" button rendered when connected has no onClick, so the mint CTA is
dead; either wire it to the mint handler (e.g., call an existing mint function
such as handleMint or mintMasterpiece from the component) by adding
onClick={handleMint} to the Button, or explicitly render a disabled/placeholder
state (disabled prop and label like "COMING SOON" and appropriate styling) until
minting is implemented; ensure you reference the same state/props used for
connectWallet and connected so the behavior is consistent.
There was a problem hiding this comment.
♻️ Duplicate comments (4)
components/ai-story-generator.tsx (4)
194-198:⚠️ Potential issue | 🟡 Minor
resetFormleaves most form state intact.Only
promptandgeneratedStoryare cleared. Fields likestoryTitle,mainCharacterName,selectedGenres,storyLength, and all pipeline parameters survive, causing the next generation to use stale values.Proposed fix
const resetForm = () => { setPrompt(''); + setStoryTitle(''); + setSelectedGenres([]); + setMainCharacterName(''); + setStoryLength('medium'); + // Reset pipeline params to defaults + setProseStyle('cinematic'); + setNarrativeVoice(''); + setSentimentTone(''); + setDarknessLevel(3); + setHumorLevel(3); + // ... remaining pipeline params setGeneratedStory(null); setActiveTab('input'); + localStorage.removeItem(DRAFT_KEY); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ai-story-generator.tsx` around lines 194 - 198, resetForm currently only clears prompt, generatedStory, and activeTab; update it to also reset all other form state so subsequent generations don't reuse stale values. In resetForm add calls to the other state setters used in this component—e.g., setStoryTitle(''), setMainCharacterName(''), setSelectedGenres([]), setStoryLength(<defaultValue>)—and reset any pipeline parameter setters (for example setTemperature, setTopP, setMaxTokens or other pipeline-related setters used in this file) back to their defaults; keep setActiveTab('input') as is.
146-152:⚠️ Potential issue | 🟡 MinorAutosave without recovery is ineffective.
Drafts are persisted to
localStoragebut there's no correspondinguseEffecton mount to restore them. Users will never see their saved work recovered—this makes the autosave logic dead code.Add recovery logic on mount:
+ // Recovery on mount + useEffect(() => { + const saved = localStorage.getItem(DRAFT_KEY); + if (saved) { + try { + const draft = JSON.parse(saved); + if (draft.prompt) setPrompt(draft.prompt); + if (draft.storyTitle) setStoryTitle(draft.storyTitle); + if (draft.selectedGenres) setSelectedGenres(draft.selectedGenres); + } catch { /* ignore parse errors */ } + } + }, []); // Autosave Logic useEffect(() => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ai-story-generator.tsx` around lines 146 - 152, The autosave useEffect writes drafts to localStorage under DRAFT_KEY but never restores them; add a mounting useEffect that reads localStorage.getItem(DRAFT_KEY), JSON.parse the value safely (try/catch), validate fields (prompt, storyTitle, selectedGenres), and call the existing state setters (setPrompt, setStoryTitle, setSelectedGenres) to repopulate the form; on parse error or invalid data, clear the stored draft or ignore it to avoid breaking the component.
319-321:⚠️ Potential issue | 🟠 Major"MINT MASTERPIECE" button has no
onClickhandler.When the wallet is connected, clicking this button does nothing. The
useWeb3hook (perweb3-provider.tsx:340-356) doesn't expose a mint function either.Either wire a mint handler or disable the button with a clear indicator:
- <Button className="bg-purple-500 text-white font-bangers text-2xl px-10 py-6 border-4 border-black shadow-[6px_6px_0px_0px_rgba(0,0,0,1)]">MINT MASTERPIECE</Button> + <Button disabled className="bg-purple-500 text-white font-bangers text-2xl px-10 py-6 border-4 border-black opacity-50 cursor-not-allowed"> + MINT MASTERPIECE (COMING SOON) + </Button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ai-story-generator.tsx` around lines 319 - 321, The "MINT MASTERPIECE" Button rendered in the AI story generator lacks an onClick handler and the useWeb3 hook does not currently expose a mint function; add a mint flow and wire it up or explicitly disable the button with a clear UI state. Concretely, either (A) extend useWeb3 to export a mintMintable or mintArtwork async function that performs the contract interaction (using the provider/signer and existing contract wrapper) and then call that function from the Button's onClick in the component, or (B) if minting isn't implemented yet, set the Button to disabled with an explanatory label/state (e.g., "Connect Wallet to Mint" or a tooltip) and ensure the component uses the hook's connection state to toggle enabled/disabled. Update the component's Button props to include onClick={handleMint} or disabled={...} and use the hook's unique symbols (useWeb3, the new mint function or connection state) to locate and wire the logic.
168-178:⚠️ Potential issue | 🟠 MajorPayload fields are silently ignored by the API.
Per
app/api/groq/route.ts:22-34, the route handler does not destructuretitleorpipelineParams. Additionally,lib/groq-service.tsexpectstone,characters, andsettinginside anoptionsobject—none of which are sent here.Concrete gaps:
Field Sent? Used by API? title✓ ✗ (ignored) pipelineParams✓ ✗ (ignored) mainCharacterName✗ Expected as charactersoptions.tone✗ Expected options.setting✗ Expected Either update the API to consume these fields, or restructure the payload to match the existing contract:
body: JSON.stringify({ action: 'generate', prompt: prompt.trim(), - title: storyTitle, genre: selectedGenres.join(', '), length: storyLength, - pipelineParams, + options: { + tone: sentimentTone, + characters: mainCharacterName, + setting: settingType, + }, }),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ai-story-generator.tsx` around lines 168 - 178, The payload sent in the fetch call in ai-story-generator.tsx (the body that includes action, prompt, title, pipelineParams, etc.) does not match the API contract used by app/api/groq/route.ts and lib/groq-service.ts (which expects an options object with tone/setting and a characters field), so update the payload to match the existing contract: replace or augment title/pipelineParams with the fields lib/groq-service.ts expects by sending characters (e.g., characters: [{ name: mainCharacterName }]) and an options object containing tone and setting (options: { tone, setting }), ensure genre/length remain mapped to the names the route handler expects, and remove or stop sending unused fields (or alternatively update app/api/groq/route.ts and lib/groq-service.ts to destructure and consume title and pipelineParams if you prefer changing the API instead).
🧹 Nitpick comments (2)
components/ai-story-generator.tsx (2)
3-3: Unused import:AnimatePresence.
AnimatePresenceis imported but not used in this component. Remove it to avoid dead code.-import { motion, AnimatePresence } from 'framer-motion'; +import { motion } from 'framer-motion';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ai-story-generator.tsx` at line 3, The import line includes an unused symbol AnimatePresence; remove AnimatePresence from the import statement (keep motion) in the module so the import becomes only motion and verify there are no remaining references to AnimatePresence in this component (e.g., search for AnimatePresence usage) before committing to avoid dead code and linter errors.
70-70: Consider stricter types for helper component props.
icon: anyandoptions: any[]lose type safety. Consider:import { LucideIcon } from 'lucide-react'; interface CollapsibleSectionProps { title: string; icon: LucideIcon; children: React.ReactNode; defaultOpen?: boolean; } interface OptionItem { value: string; label: string; }Also applies to: 83-83
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ai-story-generator.tsx` at line 70, Update the helper component and related option usage to use stricter types: replace the loose any types in CollapsibleSection's props by importing LucideIcon from lucide-react and typing the component props (e.g., CollapsibleSectionProps with title: string, icon: LucideIcon, children: React.ReactNode, defaultOpen?: boolean) and replace the options: any[] usage with a typed OptionItem interface (value: string, label: string). Apply these types to the CollapsibleSection function signature and to any variables/props named options or option items used around the CollapsibleSection and the code referenced at the other occurrence (line ~83) so the icon and option arrays are strongly typed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@components/ai-story-generator.tsx`:
- Around line 194-198: resetForm currently only clears prompt, generatedStory,
and activeTab; update it to also reset all other form state so subsequent
generations don't reuse stale values. In resetForm add calls to the other state
setters used in this component—e.g., setStoryTitle(''),
setMainCharacterName(''), setSelectedGenres([]),
setStoryLength(<defaultValue>)—and reset any pipeline parameter setters (for
example setTemperature, setTopP, setMaxTokens or other pipeline-related setters
used in this file) back to their defaults; keep setActiveTab('input') as is.
- Around line 146-152: The autosave useEffect writes drafts to localStorage
under DRAFT_KEY but never restores them; add a mounting useEffect that reads
localStorage.getItem(DRAFT_KEY), JSON.parse the value safely (try/catch),
validate fields (prompt, storyTitle, selectedGenres), and call the existing
state setters (setPrompt, setStoryTitle, setSelectedGenres) to repopulate the
form; on parse error or invalid data, clear the stored draft or ignore it to
avoid breaking the component.
- Around line 319-321: The "MINT MASTERPIECE" Button rendered in the AI story
generator lacks an onClick handler and the useWeb3 hook does not currently
expose a mint function; add a mint flow and wire it up or explicitly disable the
button with a clear UI state. Concretely, either (A) extend useWeb3 to export a
mintMintable or mintArtwork async function that performs the contract
interaction (using the provider/signer and existing contract wrapper) and then
call that function from the Button's onClick in the component, or (B) if minting
isn't implemented yet, set the Button to disabled with an explanatory
label/state (e.g., "Connect Wallet to Mint" or a tooltip) and ensure the
component uses the hook's connection state to toggle enabled/disabled. Update
the component's Button props to include onClick={handleMint} or disabled={...}
and use the hook's unique symbols (useWeb3, the new mint function or connection
state) to locate and wire the logic.
- Around line 168-178: The payload sent in the fetch call in
ai-story-generator.tsx (the body that includes action, prompt, title,
pipelineParams, etc.) does not match the API contract used by
app/api/groq/route.ts and lib/groq-service.ts (which expects an options object
with tone/setting and a characters field), so update the payload to match the
existing contract: replace or augment title/pipelineParams with the fields
lib/groq-service.ts expects by sending characters (e.g., characters: [{ name:
mainCharacterName }]) and an options object containing tone and setting
(options: { tone, setting }), ensure genre/length remain mapped to the names the
route handler expects, and remove or stop sending unused fields (or
alternatively update app/api/groq/route.ts and lib/groq-service.ts to
destructure and consume title and pipelineParams if you prefer changing the API
instead).
---
Nitpick comments:
In `@components/ai-story-generator.tsx`:
- Line 3: The import line includes an unused symbol AnimatePresence; remove
AnimatePresence from the import statement (keep motion) in the module so the
import becomes only motion and verify there are no remaining references to
AnimatePresence in this component (e.g., search for AnimatePresence usage)
before committing to avoid dead code and linter errors.
- Line 70: Update the helper component and related option usage to use stricter
types: replace the loose any types in CollapsibleSection's props by importing
LucideIcon from lucide-react and typing the component props (e.g.,
CollapsibleSectionProps with title: string, icon: LucideIcon, children:
React.ReactNode, defaultOpen?: boolean) and replace the options: any[] usage
with a typed OptionItem interface (value: string, label: string). Apply these
types to the CollapsibleSection function signature and to any variables/props
named options or option items used around the CollapsibleSection and the code
referenced at the other occurrence (line ~83) so the icon and option arrays are
strongly typed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: da875b2b-8f74-4443-8a70-c75ea168760d
📒 Files selected for processing (1)
components/ai-story-generator.tsx
Description
Issue Reference: Fixes #331
Summary of Changes:
Context / Motivation:
Type of Change
Technical Checklist
AI / Application Logic
Backend / Database
Security & Privacy
Testing Evidence
Manual Test Steps:
Visual Proof
Contributor Status
Review & Impact
Breaking Changes
Backward Compatibility / Migrations
Final Acknowledgements
Summary by CodeRabbit
New Features
Generation Improvements
Chores