Skip to content

Commit 052b4d5

Browse files
authored
unify codegen for superagent (#97)
1 parent a64e50e commit 052b4d5

File tree

7 files changed

+210
-144
lines changed

7 files changed

+210
-144
lines changed

lib/codegen-registry.ts

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Contains auto-generated codegen templates for steps with stepHandler.
88
* These templates are used when exporting workflows to standalone projects.
99
*
10-
* Generated templates: 10
10+
* Generated templates: 12
1111
*/
1212

1313
/**
@@ -567,6 +567,162 @@ export async function sendSlackMessageStep(
567567
};
568568
}
569569
}
570+
`,
571+
572+
"superagent/guard": `import { fetchCredentials } from "./lib/credential-helper";
573+
574+
function getErrorMessage(error: unknown): string {
575+
if (error instanceof Error) return error.message;
576+
return String(error);
577+
}
578+
579+
type GuardResult = {
580+
classification: GuardClassification;
581+
violationTypes: string[];
582+
cweCodes: string[];
583+
reasoning?: string;
584+
};
585+
586+
export type SuperagentGuardCoreInput = {
587+
text: string;
588+
};
589+
590+
export async function superagentGuardStep(
591+
input: SuperagentGuardCoreInput,
592+
): Promise<GuardResult> {
593+
"use step";
594+
const credentials = await fetchCredentials("superagent");
595+
const apiKey = credentials.SUPERAGENT_API_KEY;
596+
597+
if (!apiKey) {
598+
throw new Error("Superagent API Key is not configured.");
599+
}
600+
601+
try {
602+
const response = await fetch("https://app.superagent.sh/api/guard", {
603+
method: "POST",
604+
headers: {
605+
"Content-Type": "application/json",
606+
Authorization: \`Bearer \${apiKey}\`,
607+
},
608+
body: JSON.stringify({
609+
text: input.text,
610+
}),
611+
});
612+
613+
if (!response.ok) {
614+
const error = await response.text();
615+
throw new Error(\`Guard API error: \${error}\`);
616+
}
617+
618+
const data = await response.json();
619+
const choice = data.choices?.[0];
620+
const content = choice?.message?.content;
621+
622+
if (!content || typeof content !== "object") {
623+
throw new Error(
624+
"Invalid Guard API response: missing or invalid content structure",
625+
);
626+
}
627+
628+
const classification = content.classification;
629+
if (
630+
!classification ||
631+
(classification !== "allow" && classification !== "block")
632+
) {
633+
throw new Error(
634+
\`Invalid Guard API response: missing or invalid classification (received: \${JSON.stringify(classification)})\`,
635+
);
636+
}
637+
638+
return {
639+
classification,
640+
violationTypes: content?.violation_types || [],
641+
cweCodes: content?.cwe_codes || [],
642+
reasoning: choice?.message?.reasoning,
643+
};
644+
} catch (error) {
645+
throw new Error(\`Failed to analyze text: \${getErrorMessage(error)}\`);
646+
}
647+
}
648+
`,
649+
650+
"superagent/redact": `import { fetchCredentials } from "./lib/credential-helper";
651+
652+
function getErrorMessage(error: unknown): string {
653+
if (error instanceof Error) return error.message;
654+
return String(error);
655+
}
656+
657+
type RedactResult = {
658+
redactedText: string;
659+
reasoning?: string;
660+
};
661+
662+
export type SuperagentRedactCoreInput = {
663+
text: string;
664+
entities?: string[] | string;
665+
};
666+
667+
export async function superagentRedactStep(
668+
input: SuperagentRedactCoreInput,
669+
): Promise<RedactResult> {
670+
"use step";
671+
const credentials = await fetchCredentials("superagent");
672+
const apiKey = credentials.SUPERAGENT_API_KEY;
673+
674+
if (!apiKey) {
675+
throw new Error("Superagent API Key is not configured.");
676+
}
677+
678+
try {
679+
const body: { text: string; entities?: string[] } = {
680+
text: input.text,
681+
};
682+
683+
if (input.entities) {
684+
let entitiesArray: string[];
685+
686+
if (typeof input.entities === "string") {
687+
entitiesArray = input.entities.split(",").map((e) => e.trim());
688+
} else if (Array.isArray(input.entities)) {
689+
entitiesArray = input.entities.map((e) => String(e).trim());
690+
} else {
691+
entitiesArray = [];
692+
}
693+
694+
const validEntities = entitiesArray.filter((e) => e.length > 0);
695+
696+
if (validEntities.length > 0) {
697+
body.entities = validEntities;
698+
}
699+
}
700+
701+
const response = await fetch("https://app.superagent.sh/api/redact", {
702+
method: "POST",
703+
headers: {
704+
"Content-Type": "application/json",
705+
Authorization: \`Bearer \${apiKey}\`,
706+
},
707+
body: JSON.stringify(body),
708+
});
709+
710+
if (!response.ok) {
711+
const error = await response.text();
712+
throw new Error(\`Redact API error: \${error}\`);
713+
}
714+
715+
const data = await response.json();
716+
const choice = data.choices?.[0];
717+
718+
return {
719+
redactedText: choice?.message?.content || input.text,
720+
reasoning: choice?.message?.reasoning,
721+
};
722+
} catch (error) {
723+
throw new Error(\`Failed to redact text: \${getErrorMessage(error)}\`);
724+
}
725+
}
570726
`,
571727

572728
"v0/create-chat": `import { createClient, type ChatsCreateResponse } from "v0-sdk";

plugins/superagent/codegen/guard.ts

Lines changed: 0 additions & 51 deletions
This file was deleted.

plugins/superagent/codegen/redact.ts

Lines changed: 0 additions & 58 deletions
This file was deleted.

plugins/superagent/credentials.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export type SuperagentCredentials = {
2+
SUPERAGENT_API_KEY?: string;
3+
};
4+

plugins/superagent/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import type { IntegrationPlugin } from "../registry";
22
import { registerIntegration } from "../registry";
3-
import { guardCodegenTemplate } from "./codegen/guard";
4-
import { redactCodegenTemplate } from "./codegen/redact";
53
import { SuperagentIcon } from "./icon";
64

75
const superagentPlugin: IntegrationPlugin = {
@@ -54,7 +52,6 @@ const superagentPlugin: IntegrationPlugin = {
5452
rows: 4,
5553
},
5654
],
57-
codegenTemplate: guardCodegenTemplate,
5855
},
5956
{
6057
slug: "redact",
@@ -82,7 +79,6 @@ const superagentPlugin: IntegrationPlugin = {
8279
example: "",
8380
},
8481
],
85-
codegenTemplate: redactCodegenTemplate,
8682
},
8783
],
8884
};

plugins/superagent/steps/guard.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import "server-only";
33
import { fetchCredentials } from "@/lib/credential-fetcher";
44
import { type StepInput, withStepLogging } from "@/lib/steps/step-handler";
55
import { getErrorMessage } from "@/lib/utils";
6+
import type { SuperagentCredentials } from "../credentials";
67

78
type GuardClassification = "allow" | "block";
89

@@ -13,19 +14,22 @@ type GuardResult = {
1314
reasoning?: string;
1415
};
1516

16-
export type SuperagentGuardInput = StepInput & {
17-
integrationId?: string;
17+
export type SuperagentGuardCoreInput = {
1818
text: string;
1919
};
2020

21+
export type SuperagentGuardInput = StepInput &
22+
SuperagentGuardCoreInput & {
23+
integrationId?: string;
24+
};
25+
2126
/**
22-
* Guard logic - analyzes text for security threats
27+
* Core logic
2328
*/
24-
async function guard(input: SuperagentGuardInput): Promise<GuardResult> {
25-
const credentials = input.integrationId
26-
? await fetchCredentials(input.integrationId)
27-
: {};
28-
29+
async function stepHandler(
30+
input: SuperagentGuardCoreInput,
31+
credentials: SuperagentCredentials
32+
): Promise<GuardResult> {
2933
const apiKey = credentials.SUPERAGENT_API_KEY;
3034

3135
if (!apiKey) {
@@ -53,15 +57,17 @@ async function guard(input: SuperagentGuardInput): Promise<GuardResult> {
5357
const choice = data.choices?.[0];
5458
const content = choice?.message?.content;
5559

56-
// Validate response structure - fail safe instead of defaulting to "allow"
5760
if (!content || typeof content !== "object") {
5861
throw new Error(
5962
"Invalid Guard API response: missing or invalid content structure"
6063
);
6164
}
6265

6366
const classification = content.classification;
64-
if (!classification || (classification !== "allow" && classification !== "block")) {
67+
if (
68+
!classification ||
69+
(classification !== "allow" && classification !== "block")
70+
) {
6571
throw new Error(
6672
`Invalid Guard API response: missing or invalid classification (received: ${JSON.stringify(classification)})`
6773
);
@@ -79,12 +85,18 @@ async function guard(input: SuperagentGuardInput): Promise<GuardResult> {
7985
}
8086

8187
/**
82-
* Superagent Guard Step
83-
* Analyzes text for security threats like prompt injection
88+
* Step entry point
8489
*/
8590
export async function superagentGuardStep(
8691
input: SuperagentGuardInput
8792
): Promise<GuardResult> {
8893
"use step";
89-
return withStepLogging(input, () => guard(input));
94+
95+
const credentials = input.integrationId
96+
? await fetchCredentials(input.integrationId)
97+
: {};
98+
99+
return withStepLogging(input, () => stepHandler(input, credentials));
90100
}
101+
102+
export const _integrationType = "superagent";

0 commit comments

Comments
 (0)