stripe-projects
stripe projects
ai-content-generation
AI content generation with OpenAI and Claude, callAIWithPrompt usage, prompt storage in app_settings, structured outputs, response format validation, multi-criteria scoring, rate limiting, JSON schema, and AI API best practices. Use when generating content, creating prompts, scoring articles, or ...
Full skill instructions
Comprehensive guide for AI content generation in the AIProDaily platform, covering OpenAI/Claude integration, prompt management, structured outputs, and content scoring systems.
Automatically activates when:
callAIWithPrompt()app_settings AI promptsLocation: src/lib/openai.ts
import { callAIWithPrompt } from '@/lib/openai'
// Generate article title
const result = await callAIWithPrompt(
'ai_prompt_primary_article_title', // Prompt key in app_settings
newsletterId, // Tenant context
{
// Variables for placeholder replacement
title: post.title,
description: post.description,
content: post.full_article_text
}
)
// result = { headline: "AI-Generated Title" }
app_settings table by key + newsletter_id{{title}}, {{content}} with provided variablesresponse_format schema✅ Database-driven: All prompts stored in database, not hardcoded ✅ Tenant-scoped: Each newsletter can customize prompts ✅ Type-safe: JSON schema enforces response structure ✅ Flexible: Supports both OpenAI and Claude ✅ Reusable: Same function for all AI operations
-- app_settings table
CREATE TABLE app_settings (
key TEXT PRIMARY KEY,
value JSONB NOT NULL,
description TEXT,
newsletter_id UUID NOT NULL,
ai_provider TEXT, -- 'openai' or 'claude'
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
)
INSERT INTO app_settings (key, value, newsletter_id, ai_provider, description)
VALUES (
'ai_prompt_primary_article_title',
'{
"model": "gpt-4o",
"temperature": 0.7,
"max_output_tokens": 500,
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "article_title_response",
"strict": true,
"schema": {
"type": "object",
"properties": {
"headline": {
"type": "string",
"description": "The generated article headline"
}
},
"required": ["headline"],
"additionalProperties": false
}
}
},
"messages": [
{
"role": "system",
"content": "You are an expert headline writer for accounting professionals..."
},
{
"role": "user",
"content": "Source Title: {{title}}\n\nSource Content: {{content}}\n\nWrite a compelling headline."
}
]
}'::jsonb,
'newsletter-uuid-here',
'openai',
'Content Generation - Primary Article Title: Generates engaging headlines'
);
All parameters stored in database:
model - AI model to usetemperature - Creativity level (0-1)max_output_tokens - Response length limitresponse_format - JSON schema for structured outputmessages - System and user prompts with placeholders{
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "simple_response",
"strict": true,
"schema": {
"type": "object",
"properties": {
"result": { "type": "string" }
},
"required": ["result"],
"additionalProperties": false
}
}
}
}
{
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "article_body_response",
"strict": true,
"schema": {
"type": "object",
"properties": {
"headline": { "type": "string" },
"body": { "type": "string" },
"summary": { "type": "string" },
"key_points": {
"type": "array",
"items": { "type": "string" }
}
},
"required": ["headline", "body"],
"additionalProperties": false
}
}
}
}
{
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "content_score_response",
"strict": true,
"schema": {
"type": "object",
"properties": {
"score": {
"type": "number",
"minimum": 0,
"maximum": 10
},
"reasoning": { "type": "string" }
},
"required": ["score", "reasoning"],
"additionalProperties": false
}
}
}
}
Purpose: Evaluate RSS posts using multiple weighted criteria
Location: src/lib/rss-processor.ts
Storage: post_ratings table
// Criteria settings in app_settings
{
"criteria_enabled_count": 3, // 1-5 criteria
"criteria_1_name": "Interest Level",
"criteria_1_weight": 1.5,
"criteria_2_name": "Relevance",
"criteria_2_weight": 1.5,
"criteria_3_name": "Impact",
"criteria_3_weight": 1.0
}
// Each criterion gets separate AI call
for (let i = 1; i <= criteriaCount; i++) {
const promptKey = `ai_prompt_criteria_${i}`
const weight = settings[`criteria_${i}_weight`]
// Call AI for this criterion
const result = await callAIWithPrompt(
promptKey,
newsletterId,
{
title: post.title,
description: post.description,
content: post.content
}
)
// Store individual score
await supabaseAdmin
.from('post_ratings')
.insert({
post_id: post.id,
newsletter_id: newsletterId,
criterion_name: criteriaName,
score: result.score, // 0-10
weighted_score: result.score * weight,
reasoning: result.reasoning
})
}
// Calculate total score (sum of weighted scores)
const totalScore = ratings.reduce((sum, r) => sum + r.weighted_score, 0)
Criterion 1: Interest Level (weight 1.5) → score 8 → weighted 12.0
Criterion 2: Relevance (weight 1.5) → score 7 → weighted 10.5
Criterion 3: Impact (weight 1.0) → score 6 → weighted 6.0
═══════════════════════════════════════════════════════════════
Total Score: 28.5
{
"role": "system",
"content": `You are an expert content writer for accounting professionals.
Your audience is CPAs, accountants, and financial professionals.
Write in a professional yet engaging tone.
Focus on practical, actionable information.
Keep content concise and scannable.`
}
{
"role": "user",
"content": `Source Article:
Title: {{title}}
Description: {{description}}
Full Content: {{content}}
Task: Write a 200-300 word article summary that:
1. Captures the key takeaways
2. Explains why this matters to accountants
3. Uses clear, professional language
4. Ends with a thought-provoking statement
Output the summary as a JSON object with a "body" field.`
}
// Creative content (headlines, summaries)
"temperature": 0.7
// Factual content (analysis, scoring)
"temperature": 0.3
// Consistent output (classifications)
"temperature": 0.1
// Fast, cost-effective (most common)
"model": "gpt-4o"
// Latest, most capable
"model": "gpt-4o-2024-11-20"
// Smaller, faster for simple tasks
"model": "gpt-4o-mini"
// Most capable
"model": "claude-3-5-sonnet-20241022"
// Fast, cost-effective
"model": "claude-3-5-haiku-20241022"
// Older, still powerful
"model": "claude-3-opus-20240229"
try {
const result = await callAIWithPrompt(
promptKey,
newsletterId,
variables
)
// Validate response
if (!result || !result.headline) {
throw new Error('Invalid AI response: missing required fields')
}
return result
} catch (error: any) {
console.error('[AI] Error calling AI:', error.message)
// Check for specific errors
if (error.message.includes('rate_limit')) {
console.error('[AI] Rate limit exceeded, implement backoff')
}
if (error.message.includes('context_length')) {
console.error('[AI] Input too long, need to truncate')
}
throw error
}
async function callAIWithRetry(
promptKey: string,
newsletterId: string,
variables: Record<string, any>,
maxRetries = 2
) {
let retryCount = 0
while (retryCount <= maxRetries) {
try {
return await callAIWithPrompt(promptKey, newsletterId, variables)
} catch (error: any) {
retryCount++
// Don't retry on validation errors
if (error.message.includes('Invalid')) {
throw error
}
if (retryCount > maxRetries) {
throw error
}
console.log(`[AI] Retry ${retryCount}/${maxRetries} after error`)
await new Promise(resolve => setTimeout(resolve, 2000 * retryCount))
}
}
}
Tier 1 (free/trial):
Tier 2 (paid):
// Process in batches to avoid rate limits
const BATCH_SIZE = 3
const BATCH_DELAY = 2000 // 2 seconds between batches
const batches = chunkArray(posts, BATCH_SIZE)
for (const batch of batches) {
// Process batch in parallel
await Promise.all(
batch.map(post => generateArticle(post))
)
// Wait before next batch
if (batches.indexOf(batch) < batches.length - 1) {
await new Promise(resolve => setTimeout(resolve, BATCH_DELAY))
}
}
console.log(`[AI] Processed ${posts.length} items in ${batches.length} batches`)
const titleResult = await callAIWithPrompt(
'ai_prompt_primary_article_title',
newsletterId,
{
title: rssPost.title,
description: rssPost.description,
content: rssPost.full_article_text
}
)
// Store generated title
await supabaseAdmin
.from('articles')
.insert({
newsletter_id: newsletterId,
campaign_id: campaignId,
rss_post_id: rssPost.id,
headline: titleResult.headline,
article_text: null // Body generated separately
})
const bodyResult = await callAIWithPrompt(
'ai_prompt_primary_article_body',
newsletterId,
{
title: rssPost.title,
headline: article.headline, // Use AI-generated headline
description: rssPost.description,
content: rssPost.full_article_text
}
)
// Update with generated body
await supabaseAdmin
.from('articles')
.update({
article_text: bodyResult.body
})
.eq('id', article.id)
.eq('newsletter_id', newsletterId)
const factCheckResult = await callAIWithPrompt(
'ai_prompt_fact_check',
newsletterId,
{
headline: article.headline,
body: article.article_text,
source_content: article.rss_post.full_article_text
}
)
// Store fact-check score
await supabaseAdmin
.from('articles')
.update({
fact_check_score: factCheckResult.score,
fact_check_reasoning: factCheckResult.reasoning
})
.eq('id', article.id)
.eq('newsletter_id', newsletterId)
// Create test route: app/api/test/prompt/route.ts
export async function POST(request: NextRequest) {
const { promptKey, variables } = await request.json()
try {
const result = await callAIWithPrompt(
promptKey,
'test-newsletter-id',
variables
)
return NextResponse.json({
success: true,
result
})
} catch (error: any) {
return NextResponse.json({
error: error.message
}, { status: 500 })
}
}
export const maxDuration = 60
function validateArticleResponse(result: any): boolean {
if (!result) return false
if (typeof result.headline !== 'string') return false
if (typeof result.body !== 'string') return false
if (result.headline.length < 10) return false
if (result.body.length < 50) return false
return true
}
app_settings databaseCheck:
strict: true is setSolutions:
Improve:
Fix:
Main Function: src/lib/openai.ts - callAIWithPrompt()
Prompt Storage: app_settings table
Response Storage: articles, post_ratings tables
Scoring Logic: src/lib/rss-processor.ts
Related Docs:
docs/AI_PROMPT_SYSTEM_GUIDE.mddocs/OPENAI_RESPONSES_API_GUIDE.mddocs/workflows/MULTI_CRITERIA_SCORING_GUIDE.mdSkill Status: ACTIVE ✅ Line Count: < 500 ✅ Integration: OpenAI + Claude ✅
stripe projects
Harvest every `ponytail:` comment in the codebase into a debt ledger, so the deliberate shortcuts and deferrals ponytail leaves behind get tracked instead of rotting into "later means never". One-shot report, changes nothing.
okx dex market
okx agentic wallet
gmgn portfolio
okx dapp discovery
okx agent payments protocol
Stripe payment processing with checkout sessions, subscriptions, webhooks, and customer management.
Build comprehensive 3-5 year financial models with revenue projections, cost structures, cash flow analysis, and scenario planning for early-stage startups. Use this skill when creating financial projections, calculating burn rate or runway, modeling fundraising scenarios, or preparing investor-r...
Stock and cryptocurrency analysis with portfolio management and multi-dimensional scoring.
Real-time crypto derivatives positioning, whale tracking, liquidations, and institutional ETF flows across 37 specialized tools.
Master dbt (data build tool) for analytics engineering with model organization, testing, documentation, and incremental strategies. Use when building data transformations, creating data models, or implementing analytics engineering best practices.
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Stay Ahead with Finalle.ai: The Ultimate Financial News and Tools Platform
AI-powered Pine Script code generator for TradingView
Research stocks at the speed of thought.
Empower your investment strategies with AI signals, option flow data, and advanced chart patterns.
Danelfin review covering its AI stock and ETF scores, rankings, portfolio tools, current pricing, strengths, risks, and alternatives for investors.
A software for comprehensive market sentiment analysis.
'Stock Correlations' App by Freshly.ai: Reveal Stock Patterns and Trends