mirror of
https://github.com/FranP-code/inbox-negotiator.git
synced 2025-10-13 00:42:26 +00:00
Adds AI-driven conversation tracking to debt negotiation
Introduces comprehensive conversation history with a new table and UI for tracking all negotiation emails, AI analysis, and financial outcomes. Enhances real-time updates, manages negotiation rounds, and supports new statuses for negotiation lifecycle. Integrates AI-powered extraction and response analysis to automate intent detection and outcome calculations, improving transparency and automation of debt resolution.
This commit is contained in:
170
supabase/functions/test-extraction/index.ts
Normal file
170
supabase/functions/test-extraction/index.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
|
||||
import { generateObject } from "https://esm.sh/ai@3.4.7";
|
||||
import { createGoogleGenerativeAI } from "https://esm.sh/@ai-sdk/google@0.0.52";
|
||||
import { z } from "https://esm.sh/zod@3.22.4";
|
||||
|
||||
const corsHeaders = {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Headers":
|
||||
"authorization, x-client-info, apikey, content-type",
|
||||
};
|
||||
|
||||
// Same schema as analyze-response
|
||||
const responseAnalysisSchema = z.object({
|
||||
intent: z.enum([
|
||||
"acceptance",
|
||||
"rejection",
|
||||
"counter_offer",
|
||||
"request_info",
|
||||
"unclear",
|
||||
]).describe("The primary intent of the response"),
|
||||
sentiment: z.enum(["positive", "negative", "neutral"])
|
||||
.describe("Overall sentiment of the response"),
|
||||
confidence: z.number().min(0).max(1)
|
||||
.describe("Confidence in the intent classification"),
|
||||
extractedTerms: z.object({
|
||||
proposedAmount: z.number().optional().describe(
|
||||
"Any amount mentioned in response",
|
||||
),
|
||||
proposedPaymentPlan: z.string().optional().describe(
|
||||
"Payment plan details if mentioned",
|
||||
),
|
||||
paymentTerms: z.object({
|
||||
monthlyAmount: z.number().optional().describe("Monthly payment amount"),
|
||||
numberOfPayments: z.number().optional().describe(
|
||||
"Number of payments/installments",
|
||||
),
|
||||
totalAmount: z.number().optional().describe("Total amount to be paid"),
|
||||
interestRate: z.number().optional().describe(
|
||||
"Interest rate if applicable",
|
||||
),
|
||||
paymentFrequency: z.string().optional().describe(
|
||||
"Payment frequency (monthly, weekly, etc.)",
|
||||
),
|
||||
}).optional().describe("Structured payment plan terms"),
|
||||
deadline: z.string().optional().describe("Any deadline mentioned"),
|
||||
conditions: z.array(z.string()).optional().describe(
|
||||
"Any conditions or requirements mentioned",
|
||||
),
|
||||
}).describe("Key terms extracted from the response"),
|
||||
reasoning: z.string().describe("Explanation of the analysis"),
|
||||
suggestedNextAction: z.enum([
|
||||
"accept_offer",
|
||||
"send_counter",
|
||||
"request_clarification",
|
||||
"escalate_to_user",
|
||||
"mark_settled",
|
||||
]).describe("Recommended next action"),
|
||||
requiresUserReview: z.boolean().describe(
|
||||
"Whether this response needs human review",
|
||||
),
|
||||
});
|
||||
|
||||
serve(async (req) => {
|
||||
if (req.method === "OPTIONS") {
|
||||
return new Response("ok", { headers: corsHeaders });
|
||||
}
|
||||
|
||||
try {
|
||||
const { testEmail } = await req.json();
|
||||
|
||||
if (!testEmail) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: "testEmail is required" }),
|
||||
{
|
||||
status: 400,
|
||||
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const googleApiKey = Deno.env.get("GOOGLE_GENERATIVE_AI_API_KEY");
|
||||
|
||||
if (!googleApiKey) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: "Google API key not configured" }),
|
||||
{
|
||||
status: 500,
|
||||
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
console.log("Testing extraction with email:", testEmail);
|
||||
|
||||
const result = await generateObject({
|
||||
model: createGoogleGenerativeAI({
|
||||
apiKey: googleApiKey,
|
||||
})("gemini-2.5-flash-preview-04-17"),
|
||||
system: `You are an expert financial analyst specializing in debt collection and negotiation responses.
|
||||
Your job is to carefully analyze creditor responses and extract ALL financial terms mentioned.
|
||||
|
||||
CRITICAL: Always extract financial information when present. Look for:
|
||||
|
||||
AMOUNTS:
|
||||
- Any dollar amounts mentioned ($1,000, $500, etc.)
|
||||
- Settlement offers or counter-offers
|
||||
- Monthly payment amounts
|
||||
- Total payment amounts
|
||||
|
||||
PAYMENT PLANS:
|
||||
- Monthly payment amounts (e.g., "$200 per month", "$150/month")
|
||||
- Number of payments/installments (e.g., "12 months", "24 payments")
|
||||
- Payment frequency (monthly, weekly, bi-weekly)
|
||||
- Total amounts for payment plans
|
||||
- Interest rates if mentioned
|
||||
|
||||
EXTRACT EVERYTHING: Even if amounts seem obvious, always include them in extractedTerms.`,
|
||||
prompt: `Analyze this test email and extract ALL financial terms:
|
||||
|
||||
EMAIL: ${testEmail}
|
||||
|
||||
EXTRACTION REQUIREMENTS:
|
||||
1. Find ANY dollar amounts mentioned in the email
|
||||
2. Look for payment plan details (monthly amounts, number of payments)
|
||||
3. Identify payment frequency (monthly, weekly, etc.)
|
||||
4. Extract total amounts if mentioned
|
||||
5. Note any interest rates or fees
|
||||
6. Capture all conditions and requirements
|
||||
|
||||
EXAMPLES OF WHAT TO EXTRACT:
|
||||
- "We can accept $250 per month" → monthlyAmount: 250
|
||||
- "for 18 months" → numberOfPayments: 18
|
||||
- "totaling $4,500" → totalAmount: 4500
|
||||
- "settlement of $3,200" → proposedAmount: 3200
|
||||
- "monthly payments" → paymentFrequency: "monthly"
|
||||
|
||||
Be thorough and extract ALL financial information present in the email.`,
|
||||
schema: responseAnalysisSchema,
|
||||
});
|
||||
|
||||
console.log("AI Analysis Result:", JSON.stringify(result.object, null, 2));
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
analysis: result.object,
|
||||
extractedTerms: result.object.extractedTerms,
|
||||
debug: {
|
||||
emailLength: testEmail.length,
|
||||
hasGoogleAPI: !!googleApiKey,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}),
|
||||
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error in test-extraction function:", error);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: "Internal server error",
|
||||
details: error.message
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user