mirror of
https://github.com/domfelipe/mika-agent-assist.git
synced 2026-08-07 10:16:43 +00:00
Changes
Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com>
This commit is contained in:
parent
2c5ca61b19
commit
71d0c59d69
6 changed files with 466 additions and 0 deletions
114
supabase/functions/generate-skill-markdown/index.ts
Normal file
114
supabase/functions/generate-skill-markdown/index.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// Gera o markdown de uma skill no padrão agentskills.io via Lovable AI Gateway.
|
||||
import { corsHeaders } from "../_shared/cors.ts";
|
||||
|
||||
const LOVABLE_API_KEY = Deno.env.get("LOVABLE_API_KEY")!;
|
||||
const MODEL = "google/gemini-2.5-flash";
|
||||
const MAX_LEN = 50000;
|
||||
|
||||
const SYSTEM_PROMPT = `Você é um especialista em criar skills para o Hermes Agent no padrão agentskills.io. Gere um arquivo markdown completo e bem estruturado para a skill descrita pelo usuário. O arquivo deve seguir esta estrutura exata: cabeçalho YAML com name, description, trigger_keywords; seção ## Quando usar; seção ## Inputs esperados (se houver); seção ## Passo a passo numerada; seção ## Ferramentas necessárias; seção ## Critério de sucesso; seção opcional ## Exemplo. Use linguagem clara, imperativa e em português. Não adicione comentários fora do markdown. Retorne APENAS o conteúdo do arquivo .md, sem code fences.`;
|
||||
|
||||
interface FormInputs {
|
||||
name: string;
|
||||
description: string;
|
||||
trigger_keywords: string;
|
||||
expected_inputs?: string | null;
|
||||
steps: string;
|
||||
required_tools: string[];
|
||||
success_criteria: string;
|
||||
example_use_case?: string | null;
|
||||
}
|
||||
|
||||
function formatUserMessage(f: FormInputs): string {
|
||||
return [
|
||||
`Nome: ${f.name}`,
|
||||
`Descrição: ${f.description}`,
|
||||
`Palavras-chave de gatilho: ${f.trigger_keywords}`,
|
||||
f.expected_inputs ? `Inputs esperados: ${f.expected_inputs}` : null,
|
||||
`Passo a passo:\n${f.steps}`,
|
||||
`Ferramentas necessárias: ${f.required_tools.join(", ")}`,
|
||||
`Critério de sucesso: ${f.success_criteria}`,
|
||||
f.example_use_case ? `Exemplo de uso: ${f.example_use_case}` : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n\n");
|
||||
}
|
||||
|
||||
Deno.serve(async (req) => {
|
||||
if (req.method === "OPTIONS") {
|
||||
return new Response(null, { headers: corsHeaders });
|
||||
}
|
||||
|
||||
try {
|
||||
const body = await req.json();
|
||||
const form_inputs = body?.form_inputs as FormInputs | undefined;
|
||||
|
||||
if (!form_inputs?.name || !form_inputs?.description || !form_inputs?.steps) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: "form_inputs incompleto" }),
|
||||
{ status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } },
|
||||
);
|
||||
}
|
||||
|
||||
const aiRes = await fetch("https://ai.gateway.lovable.dev/v1/chat/completions", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${LOVABLE_API_KEY}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: MODEL,
|
||||
messages: [
|
||||
{ role: "system", content: SYSTEM_PROMPT },
|
||||
{ role: "user", content: formatUserMessage(form_inputs) },
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
if (aiRes.status === 429) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: "Muitas requisições. Aguarde 1 minuto e tente novamente." }),
|
||||
{ status: 429, headers: { ...corsHeaders, "Content-Type": "application/json" } },
|
||||
);
|
||||
}
|
||||
if (aiRes.status === 402) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: "Crédito de IA esgotado. Entre em contato com o suporte." }),
|
||||
{ status: 402, headers: { ...corsHeaders, "Content-Type": "application/json" } },
|
||||
);
|
||||
}
|
||||
if (!aiRes.ok) {
|
||||
const txt = await aiRes.text();
|
||||
console.error("AI gateway error:", aiRes.status, txt);
|
||||
return new Response(
|
||||
JSON.stringify({ error: "Falha ao gerar skill. Tente novamente." }),
|
||||
{ status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } },
|
||||
);
|
||||
}
|
||||
|
||||
const data = await aiRes.json();
|
||||
let markdown_content: string = data?.choices?.[0]?.message?.content ?? "";
|
||||
markdown_content = markdown_content.trim();
|
||||
|
||||
if (!markdown_content) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: "A IA retornou conteúdo vazio. Tente novamente." }),
|
||||
{ status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } },
|
||||
);
|
||||
}
|
||||
|
||||
if (markdown_content.length > MAX_LEN) {
|
||||
markdown_content = markdown_content.slice(0, MAX_LEN);
|
||||
}
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({ markdown_content }),
|
||||
{ status: 200, headers: { ...corsHeaders, "Content-Type": "application/json" } },
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("generate-skill-markdown error:", e);
|
||||
return new Response(
|
||||
JSON.stringify({ error: "Erro inesperado ao gerar skill." }),
|
||||
{ status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } },
|
||||
);
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue