📦 EqualifyEverything / equalify

📄 getBlockerSummary.ts · 118 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118import { db, event, graphqlQuery, bedrock } from "#src/utils";

export const getBlockerSummary = async () => {
  const { blocker_id, refresh } = event.queryStringParameters as {
    blocker_id: string;
    refresh?: string;
  };

  await db.connect();

  try {
    const optionsData = await graphqlQuery({
      query: `query {
                options(where: { key: { _in: ["llm_enabled", "llm_model_id"] } }) {
                    key
                    value
                }
            }`,
    });
    const optMap = Object.fromEntries(
      (optionsData?.options ?? []).map((o: any) => [o.key, o.value]),
    );
    if (optMap.llm_enabled === "false") {
      return { disabled: true };
    }
    const modelId = optMap.llm_model_id || undefined;

    if (refresh !== "true") {
      const existing = (
        await db.query({
          text: `SELECT "id", "summary", "flagged" FROM "blocker_llm_summaries" WHERE "blocker_id" = $1 ORDER BY "created_at" DESC LIMIT 1`,
          values: [blocker_id],
        })
      ).rows[0];

      if (existing?.flagged) {
        return { flagged: true };
      }

      if (existing) {
        return { id: existing.id, summary: existing.summary, cached: true };
      }
    }

    const data = await graphqlQuery({
      query: `query($id: uuid!) {
                blockers_by_pk(id: $id) {
                    content
                    url { url }
                    blocker_messages {
                        message {
                            content
                            category
                            message_tags { tag { content } }
                        }
                    }
                }
            }`,
      variables: { id: blocker_id },
    });

    const blocker = data?.blockers_by_pk;
    if (!blocker) {
      return {
        statusCode: 404,
        body: JSON.stringify({ error: "Blocker not found" }),
      };
    }

    const messages = blocker.blocker_messages
      .map(
        (bm: any) =>
          `Error: ${bm.message.content} (Category: ${bm.message.category}, Tags: ${bm.message.message_tags.map((t: any) => t.tag.content).join(", ")})`,
      )
      .join("\n");

    const prompt = `You are an accessibility expert helping a website owner fix an issue detected by an automated accessibility scanner.

The issue was found on this URL: ${blocker.url.url}

Accessibility error details:
${messages}

Affected HTML:
\`\`\`html
${blocker.content}
\`\`\`

Please provide:
1. A plain-language explanation of what this accessibility issue is and why it matters to users.
2. Clear step-by-step instructions to fix it.

Be concise and practical. Target a general audience. This is for use in a UI context, so do not be conversational and begin or end with a question.`;

    const summary = await bedrock.invoke(prompt, modelId);

    const result = (
      await db.query({
        text: `INSERT INTO "blocker_llm_summaries" ("blocker_id", "summary")
                   VALUES ($1, $2)
                   ON CONFLICT ("blocker_id") DO UPDATE SET "summary" = $2, "flagged" = false, "updated_at" = now()
                   RETURNING "id"`,
        values: [blocker_id, summary],
      })
    ).rows[0];

    return { id: result.id, summary, cached: false };
  } catch (err) {
    console.error("getBlockerSummary error:", err);
    return {
      statusCode: 500,
      body: JSON.stringify({ error: String(err) }),
    };
  } finally {
    await db.clean();
  }
};