Skip to content

Commit 9bd40c1

Browse files
chad-syntaxChad $yntax
andauthored
Chad/prototype (#1)
* start work on prototype * start of prototype, gen types, sdk, tests * added openrouter and prompt tester * start on more robust data schema and api routes * cursor written api endpoint tests * move files around, add placeholders for bullmq, rest services * fix nextjs public folder * slight adjustment to typegen cmd * path fixes for build passing * add supabase github actions, and first migration, delete agent rest api exploration * fix gh actions directory * seeding and migration changes * fucking tests man * add llm logs table * sign in with github auth * add tests to gh actions * try updating cli version for running tests * try latest? * refactored openrouter implementation to work with vault and code verifier per user * add pgTAP tests for new rpc fns * consolidate supabase migration check and tests into one workflow * rename workflow * test prompt running * change app to studio routes, add prompt pages from supabase, add llm logs view, RLS, and RLS tests * start onboarding logic * upgrade tailwind to v4, add onboarding logic * add org and project switcher, adjust client side context, consolidate migration, refactor user_keys into organization_keys * refactor routes * layout and context kinda fixed * adjust prompt schema, adjust openrouter calling, fix routing on some pages * fix ts err for now * openrouter clean up, org page listing users * prompts pages, template extracting, component consolidation * remove uneeded code * prompt editorial workflow fixes * add api tests * move util * shadcn overhaul, colors, and themes * refactor keys so orgs get an sdk api key on creation, allow for users to execute via api key, add api key reveal component to org page * fix prompt config saving, add additional openrouter typing * finish services refactor * progress towards github repo sync * start of sync work that was vibing so it probably doesnt work * refactor migration into using schemas * remove test migration, replace initial schema migration * attempt to pin postgres-version because of permissions error * Refactor GitHub App installation flow Create a pending installation record before redirecting to GitHub. Verify the installation on callback against the pending record and GitHub user installations. On successful verification, activate the installation and create repository records for all accessible repositories linked to the organization. * Create .prettierrc.json * Github App installation record flow working (still need to save and refresh provider tokens in future update, right now it will only work if we install the app directly after logging in before the token expires) Got github webhooks updating the installation record working with a github_webhook service account and role. * Remove supabase declarative schemas, still kinda buggy refactor installation validation to just check for the installation via app REST request rather than user oauth REST request adjust the connecting modal * Refactor: Centralize external URLs in routes utility Consolidate GitHub and OpenRouter URLs into a new `src/utils/routes.ts` utility for improved maintainability and consistency across the codebase. Update relevant services and components to use the new routes object. Pass project UUID during GitHub project connection. Add UI check for incomplete GitHub app installation. * Add GitHub repository sync functionality This provides project-level sync capability with GitHub and improves repository connection handling. Changes include: - Add sync button and action to project page - Enhance repository sync logic - Add unique constraint to active GitHub app installations - Fix installation ID handling - Also add tests for github_app_installations and project_repositories * Enhance project connection and repository handling This update introduces several improvements to the project connection process and repository management. Key changes include: - Added organization UUID to the connectProject function. - Updated the ConnectProjectModal to handle default repository IDs. - Enhanced GitHub service to store repository default branch information. - Improved error handling in prompt version creation and updates. - Refactored code for better readability and maintainability. These changes aim to streamline the integration with GitHub and improve user experience when connecting projects. * Update project_repositories test to include default branch This commit enhances the project_repositories test by adding the repository_default_branch field to the insert statements. The default branch is now set to 'main' for all test cases, ensuring consistency and accuracy in repository data handling during tests. * Refactor GitHub services and update user authentication handling This commit includes significant refactoring of the GitHub services, transitioning from a single GitHubService to multiple specialized services: GitHubAppService, GitHubWebhookService, and GitHubSyncService. Additionally, the user authentication process has been streamlined by replacing the initialize method with a getAuthUser method across various components and services. The connectProject action has been moved, and related imports have been updated accordingly. These changes aim to enhance code organization and improve the overall user experience. * Update environment configuration and enhance template variable handling This commit modifies the environment configuration to load the default `.env` file in the smee proxy and updates the template variable extraction logic to include a `default_value` field for all variables. Additionally, new types and relationships have been added to the Supabase schema for agentsmith events, for logging sync events. Added tests for variable compiling. * Big GitHubSync Service refactor Enhance project data retrieval and user notifications This commit updates the project data retrieval methods to use UUIDs consistently across the application, improving data integrity and reducing errors. Additionally, it introduces a toast notification system to inform users about project sync events, enhancing user experience. The layout has been updated to include a Toaster component for displaying notifications. Other minor refactorings and optimizations have been made throughout the codebase to improve maintainability. * Janky but working version of repo -> agentsmith syncing * complete sync service refactor * add support for global context * add erorr alert for authoring with missing globals * refactor a lot of fe component names, add studio header and hooks * styling refactors * logger refactor * add emoji mode * fix migration ordering * comment out test for now * adjust supabase migrations cicd yml * adjust landing styling --------- Co-authored-by: Chad $yntax <chad@ttpspodcast.com>
0 parents  commit 9bd40c1

File tree

3 files changed

+469
-0
lines changed

3 files changed

+469
-0
lines changed

generated_agentsmith.types.ts

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
// ⚡️ Generated by AgentSmith ⚡️
2+
// ⚠️ Do not edit this file directly ⚠️
3+
export type PromptSlug =
4+
| 'qualify_lead'
5+
| 'detect_super_qualified_lead'
6+
| 'lead_urgency_assessment'
7+
| 'budget_alignment_verification'
8+
| 'technical_fit_assessment'
9+
| 'default_system_prompt';
10+
export type Prompt = {
11+
id: string;
12+
name: string;
13+
content: string;
14+
version: string;
15+
variables: PromptVariable[];
16+
slug: PromptSlug;
17+
};
18+
export type PromptVariable = {
19+
name: string;
20+
type: 'string' | 'number' | 'boolean';
21+
required: boolean;
22+
};
23+
export type AgentAction = {
24+
id: string;
25+
name: string;
26+
slug: string;
27+
prompt_id: string;
28+
};
29+
export type AGENT_TRIGGERS = {
30+
AFTER_ANY_MESSAGE: 'AFTER_ANY_MESSAGE';
31+
BEFORE_ANY_MESSAGE: 'BEFORE_ANY_MESSAGE';
32+
AFTER_USER_MESSAGE: 'AFTER_USER_MESSAGE';
33+
AFTER_ASSISTANT_MESSAGE: 'AFTER_ASSISTANT_MESSAGE';
34+
BEFORE_USER_MESSAGE: 'BEFORE_USER_MESSAGE';
35+
BEFORE_ASSISTANT_MESSAGE: 'BEFORE_ASSISTANT_MESSAGE';
36+
BEFORE_INSTANCE_CLEANUP: 'BEFORE_INSTANCE_CLEANUP';
37+
CRON_SCHEDULE: 'CRON_SCHEDULE';
38+
};
39+
export type AgentTriggers = keyof AGENT_TRIGGERS;
40+
export type AgentTrigger = {
41+
type: AgentTriggers;
42+
cron_schedule?: string;
43+
} & (
44+
| {
45+
type: 'CRON_SCHEDULE';
46+
cron_schedule: string;
47+
}
48+
| {
49+
type: Exclude<AgentTriggers, 'CRON_SCHEDULE'>;
50+
cron_schedule?: never;
51+
}
52+
);
53+
export type AgentReaction = {
54+
id: string;
55+
name: string;
56+
slug: string;
57+
prompt_id: string;
58+
triggers: AgentTrigger[];
59+
};
60+
export type Agent = {
61+
id: string;
62+
name: string;
63+
slug: string;
64+
system_prompt_id: string;
65+
actions: AgentAction[];
66+
reactions: AgentReaction[];
67+
};
68+
export type Agency = {
69+
prompts: {
70+
qualify_lead: Prompt & {
71+
id: 'a1b2c3d4-e5f6-4321-8901-abcdef123456';
72+
name: 'Qualify Lead';
73+
content: string;
74+
version: '1.0.0';
75+
variables: [
76+
{
77+
name: 'company_info';
78+
type: 'string';
79+
required: true;
80+
},
81+
{
82+
name: 'contact_name';
83+
type: 'string';
84+
required: true;
85+
},
86+
{
87+
name: 'budget_range';
88+
type: 'string';
89+
required: true;
90+
},
91+
];
92+
slug: 'qualify_lead';
93+
};
94+
detect_super_qualified_lead: Prompt & {
95+
id: 'b2c3d4e5-f6a7-5432-9012-bcdef234567';
96+
name: 'Detect Super Qualified Lead';
97+
content: string;
98+
version: '1.0.0';
99+
variables: [
100+
{
101+
name: 'lead_details';
102+
type: 'string';
103+
required: true;
104+
},
105+
{
106+
name: 'budget_status';
107+
type: 'string';
108+
required: true;
109+
},
110+
{
111+
name: 'technical_requirements';
112+
type: 'string';
113+
required: true;
114+
},
115+
];
116+
slug: 'detect_super_qualified_lead';
117+
};
118+
lead_urgency_assessment: Prompt & {
119+
id: 'c3d4e5f6-g7h8-6543-0123-cdefg345678';
120+
name: 'Lead Urgency Assessment';
121+
content: string;
122+
version: '1.0.0';
123+
variables: [
124+
{
125+
name: 'current_situation';
126+
type: 'string';
127+
required: true;
128+
},
129+
{
130+
name: 'timeline';
131+
type: 'string';
132+
required: true;
133+
},
134+
{
135+
name: 'competition_info';
136+
type: 'string';
137+
required: true;
138+
},
139+
];
140+
slug: 'lead_urgency_assessment';
141+
};
142+
budget_alignment_verification: Prompt & {
143+
id: 'd4e5f6g7-h8i9-7654-2345-defgh456789';
144+
name: 'Budget Alignment Verification';
145+
content: string;
146+
version: '1.0.0';
147+
variables: [
148+
{
149+
name: 'budget_details';
150+
type: 'string';
151+
required: true;
152+
},
153+
{
154+
name: 'current_spend';
155+
type: 'string';
156+
required: true;
157+
},
158+
{
159+
name: 'decision_timeline';
160+
type: 'string';
161+
required: true;
162+
},
163+
];
164+
slug: 'budget_alignment_verification';
165+
};
166+
technical_fit_assessment: Prompt & {
167+
id: 'e5f6g7h8-i9j0-8765-3456-efghi567890';
168+
name: 'Technical Fit Assessment';
169+
content: string;
170+
version: '1.0.0';
171+
variables: [
172+
{
173+
name: 'tech_stack';
174+
type: 'string';
175+
required: true;
176+
},
177+
{
178+
name: 'integration_needs';
179+
type: 'string';
180+
required: true;
181+
},
182+
{
183+
name: 'technical_constraints';
184+
type: 'string';
185+
required: true;
186+
},
187+
];
188+
slug: 'technical_fit_assessment';
189+
};
190+
default_system_prompt: Prompt & {
191+
id: 'b2c3d4e5-f6g7-5432-9012-bcdef234567';
192+
name: 'Default system prompt';
193+
content: string;
194+
version: '1.0.0';
195+
variables: [];
196+
slug: 'default_system_prompt';
197+
};
198+
};
199+
agents: {
200+
lead_agent: Agent & {
201+
id: 'f6g7h8i9-j0k1-9876-4567-fghij678901';
202+
name: 'Lead Agent';
203+
slug: 'lead_agent';
204+
system_prompt_id: 'b2c3d4e5-f6g7-5432-9012-bcdef234567';
205+
actions: [
206+
{
207+
id: 'c8d9e0f1-g2h3-7654-3210-ghijkl456789';
208+
name: 'Qualify Lead';
209+
slug: 'qualify_lead';
210+
prompt_id: 'a1b2c3d4-e5f6-4321-8901-abcdef123456';
211+
},
212+
];
213+
reactions: [
214+
{
215+
id: 'd4e5f6g7-h8i9-8765-4321-ijklmn890123';
216+
name: 'Detect Super Qualified Lead';
217+
slug: 'detect_super_qualified_lead';
218+
prompt_id: 'b2c3d4e5-f6a7-5432-9012-bcdef234567';
219+
triggers: [{ type: 'AFTER_ASSISTANT_MESSAGE' }];
220+
},
221+
];
222+
};
223+
};
224+
};

sdk.test.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { AgentsmithClient } from './sdk';
2+
import { Agency } from './generated_agentsmith.types';
3+
4+
describe('AgentsmithClient', () => {
5+
let client: AgentsmithClient<Agency>;
6+
7+
beforeEach(() => {
8+
client = new AgentsmithClient<Agency>('test-api-key');
9+
});
10+
11+
it('should get and compile prompts with type safety', async () => {
12+
const prompt = await client.getPrompt('qualify_lead');
13+
const compiledPrompt = prompt.compile({
14+
company_info: 'Acme Corp',
15+
contact_name: 'John Doe',
16+
budget_range: '$100k-$500k',
17+
});
18+
expect(compiledPrompt).toBeDefined();
19+
expect(compiledPrompt).toContain('Acme Corp');
20+
expect(compiledPrompt).toContain('John Doe');
21+
});
22+
23+
it("should throw an error if getPrompt is called with a prompt slug that doesn't exist", async () => {
24+
await expect(
25+
// Need to cast to any to test runtime behavior
26+
(client.getPrompt as any)('non_existent_prompt')
27+
).rejects.toThrow('Prompt non_existent_prompt not found');
28+
});
29+
30+
it('should throw an error if passed a variable that is not a variable of the prompt', async () => {
31+
const prompt = await client.getPrompt('qualify_lead');
32+
expect(() => (prompt.compile as any)({ invalid_variable: 'test' })).toThrow(
33+
'Unknown variable: invalid_variable'
34+
);
35+
});
36+
37+
it('should throw an error if passed a variable of the wrong type', async () => {
38+
const prompt = await client.getPrompt('qualify_lead');
39+
const wrongTypeVars = {
40+
company_info: 123 as any, // Explicitly cast to any to test runtime behavior
41+
contact_name: 'John',
42+
budget_range: '$100k',
43+
};
44+
expect(() => prompt.compile(wrongTypeVars)).toThrow(
45+
'Invalid type for variable company_info: expected string, got number'
46+
);
47+
});
48+
49+
it('should throw an error if required variable is not passed', async () => {
50+
const prompt = await client.getPrompt('qualify_lead');
51+
const incompleteVars = {
52+
company_info: 'Acme Corp',
53+
budget_range: '$100k',
54+
} as any; // Cast to any to bypass type checking for test
55+
expect(() => prompt.compile(incompleteVars)).toThrow(
56+
'Missing required variable: contact_name'
57+
);
58+
});
59+
60+
it('should enforce type safety for agent actions', async () => {
61+
const agent = await client.getAgent('lead_agent');
62+
const instance = await agent.createInstance({ user: { id: '123' } });
63+
64+
const response = await instance.action('qualify_lead', {
65+
company_info: 'Acme Corp',
66+
contact_name: 'John Doe',
67+
budget_range: '$100k-$500k',
68+
});
69+
70+
expect(response.content).toBeDefined();
71+
expect(response.content).toContain('Mocked response for qualify_lead');
72+
});
73+
74+
it("should throw an error if getAgent is called with an agent slug that doesn't exist", async () => {
75+
await expect(
76+
// Need to cast to any to test runtime behavior
77+
(client.getAgent as any)('non_existent_agent')
78+
).rejects.toThrow('Agent non_existent_agent not found');
79+
});
80+
81+
it('should allow creating agent instance with custom context', async () => {
82+
const agent = await client.getAgent('lead_agent');
83+
const customContext = {
84+
user: { id: '123', name: 'Test User' },
85+
custom_field: 'test value',
86+
};
87+
const instance = await agent.createInstance(customContext);
88+
const response = await instance.action('qualify_lead', {
89+
company_info: 'Acme Corp',
90+
contact_name: 'John Doe',
91+
budget_range: '$100k-$500k',
92+
});
93+
expect(response.content).toBeDefined();
94+
});
95+
96+
it('should throw an error if a required string variable is empty', async () => {
97+
const prompt = await client.getPrompt('qualify_lead');
98+
const varsWithEmptyString = {
99+
company_info: '', // empty string for required field
100+
contact_name: 'John Doe',
101+
budget_range: '$100k-$500k',
102+
};
103+
expect(() => prompt.compile(varsWithEmptyString)).toThrow(
104+
'Required string variable company_info cannot be empty'
105+
);
106+
});
107+
});

0 commit comments

Comments
 (0)