Skip to content

Commit f107d2a

Browse files
Merge pull request #5 from aipotheosis-labs/more_code_example
clean code
2 parents 5e4d44b + 6f07e43 commit f107d2a

24 files changed

+242
-256
lines changed

.eslintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"linebreak-style": ["error", "unix"],
2323
"quotes": ["error", "single"],
2424
"semi": ["error", "always"],
25-
"@typescript-eslint/no-explicit-any": "warn",
25+
"@typescript-eslint/no-explicit-any": "off",
2626
"@typescript-eslint/explicit-function-return-type": "off",
2727
"@typescript-eslint/no-unused-vars": "warn"
2828
},

DEVELOPMENT.md renamed to CONTRIBUTING.md

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ cp .env.example .env
3030

3131
2. Add the following environment variables to your `.env` file:
3232

33-
Replace `your_api_key_here` with your actual ACI API key.
3433

3534
## Development
3635

@@ -50,6 +49,7 @@ This will compile the TypeScript code into JavaScript in the `dist` directory.
5049
> - ARXIV
5150
> - BRAVE_SEARCH
5251
> - GITHUB
52+
> We might need to change test logic so you don't need to do this in the future.
5353
5454
The project uses Jest for testing. To run the tests:
5555
```bash
@@ -58,7 +58,7 @@ pnpm test
5858

5959
To run tests in watch mode during development:
6060
```bash
61-
pnpm test -- --watch
61+
pnpm runtest
6262
```
6363

6464
### Code Quality
@@ -78,6 +78,8 @@ pnpm run format
7878
## Project Structure
7979

8080
```
81+
__tests__/ # Test files
82+
examples/ # Examples
8183
src/
8284
├── index.ts # Main entry point
8385
├── client.ts # API client implementation
@@ -92,29 +94,14 @@ src/
9294
## Publishing
9395

9496
The project uses Changesets for versioning and publishing. To create a new version:
97+
You need change the version in the `package.json` then commit and push to the main branch.
98+
The CI will build the project and publish the new version to npm.
9599

96-
1. Create a changeset:
97-
```bash
98-
pnpm run changeset
99-
```
100100

101-
2. Update the version:
102-
```bash
103-
pnpm run version
104-
```
105-
106-
3. Publish to npm:
107-
```bash
108-
pnpm run release
109-
```
110101

111102
## Contributing
112103

113104
1. Create a new branch for your feature or bugfix
114105
2. Make your changes
115106
3. Run tests and ensure they pass
116-
4. Submit a pull request
117-
118-
## License
119-
120-
This project is licensed under the MIT License - see the LICENSE file for details.
107+
4. Submit a pull request

__tests__/meta_functions/aci_execute_function_mock.test.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { FunctionDefinitionFormat } from '../../src/types/functions';
2-
import {
3-
ACIExecuteFunction,
4-
getAciExecuteFunctionSchema,
5-
wrapFunctionArgumentsIfNotPresent
6-
} from '../../src/meta_functions/aci_execute_function';
2+
import {
3+
ACIExecuteFunction,
4+
getAciExecuteFunctionSchema,
5+
wrapFunctionArgumentsIfNotPresent,
6+
} from '../../src/meta_functions/aci-execute-function';
77
import { ACI } from '../../src/client';
88
import MockAdapter from 'axios-mock-adapter';
99

@@ -55,16 +55,16 @@ describe('ACI_EXECUTE_FUNCTION Meta Function', () => {
5555
const input = {
5656
function_name: 'calendar_create_event',
5757
title: 'Meeting',
58-
start_time: '2023-01-01T10:00:00Z'
58+
start_time: '2023-01-01T10:00:00Z',
5959
};
6060

6161
const result = wrapFunctionArgumentsIfNotPresent(input);
6262
expect(result).toEqual({
6363
function_name: 'calendar_create_event',
6464
function_arguments: {
6565
title: 'Meeting',
66-
start_time: '2023-01-01T10:00:00Z'
67-
}
66+
start_time: '2023-01-01T10:00:00Z',
67+
},
6868
});
6969
});
7070

@@ -73,8 +73,8 @@ describe('ACI_EXECUTE_FUNCTION Meta Function', () => {
7373
function_name: 'calendar_create_event',
7474
function_arguments: {
7575
title: 'Meeting',
76-
start_time: '2023-01-01T10:00:00Z'
77-
}
76+
start_time: '2023-01-01T10:00:00Z',
77+
},
7878
};
7979

8080
const result = wrapFunctionArgumentsIfNotPresent(input);
@@ -85,7 +85,7 @@ describe('ACI_EXECUTE_FUNCTION Meta Function', () => {
8585
describe('Integration with client', () => {
8686
const mockResponse = {
8787
success: true,
88-
data: { id: '123', title: 'Meeting' }
88+
data: { id: '123', title: 'Meeting' },
8989
};
9090

9191
it('should call the execute endpoint with the correct parameters', async () => {
@@ -99,10 +99,10 @@ describe('ACI_EXECUTE_FUNCTION Meta Function', () => {
9999
function_arguments: {
100100
title: 'Team Meeting',
101101
start_time: '2023-01-01T10:00:00Z',
102-
end_time: '2023-01-01T11:00:00Z'
103-
}
102+
end_time: '2023-01-01T11:00:00Z',
103+
},
104104
},
105-
linkedAccountOwnerId
105+
linkedAccountOwnerId,
106106
});
107107

108108
expect(mock.history.post.length).toBe(1);
@@ -111,9 +111,9 @@ describe('ACI_EXECUTE_FUNCTION Meta Function', () => {
111111
function_input: {
112112
title: 'Team Meeting',
113113
start_time: '2023-01-01T10:00:00Z',
114-
end_time: '2023-01-01T11:00:00Z'
114+
end_time: '2023-01-01T11:00:00Z',
115115
},
116-
linked_account_owner_id: linkedAccountOwnerId
116+
linked_account_owner_id: linkedAccountOwnerId,
117117
});
118118
expect(result).toEqual(mockResponse);
119119
});
@@ -128,27 +128,27 @@ describe('ACI_EXECUTE_FUNCTION Meta Function', () => {
128128
functionArguments: {
129129
function_name: 'calendar_create_event',
130130
title: 'Team Meeting',
131-
start_time: '2023-01-01T10:00:00Z'
131+
start_time: '2023-01-01T10:00:00Z',
132132
},
133-
linkedAccountOwnerId
133+
linkedAccountOwnerId,
134134
});
135135

136136
expect(mock.history.post.length).toBe(1);
137137
expect(mock.history.post[0].url).toBe('/functions/calendar_create_event/execute');
138138
expect(JSON.parse(mock.history.post[0].data)).toEqual({
139139
function_input: {
140140
title: 'Team Meeting',
141-
start_time: '2023-01-01T10:00:00Z'
141+
start_time: '2023-01-01T10:00:00Z',
142142
},
143-
linked_account_owner_id: linkedAccountOwnerId
143+
linked_account_owner_id: linkedAccountOwnerId,
144144
});
145145
expect(result).toEqual(mockResponse);
146146
});
147147

148148
it('should handle function execution errors', async () => {
149149
const errorResponse = {
150150
success: false,
151-
error: 'Invalid parameters'
151+
error: 'Invalid parameters',
152152
};
153153

154154
mock.onPost('/functions/calendar_create_event/execute').reply(200, errorResponse);
@@ -158,13 +158,13 @@ describe('ACI_EXECUTE_FUNCTION Meta Function', () => {
158158
functionName: 'ACI_EXECUTE_FUNCTION',
159159
functionArguments: {
160160
function_name: 'calendar_create_event',
161-
function_arguments: { title: 'Meeting' }
161+
function_arguments: { title: 'Meeting' },
162162
},
163-
linkedAccountOwnerId
163+
linkedAccountOwnerId,
164164
});
165165

166166
expect(mock.history.post.length).toBe(1);
167167
expect(result).toEqual(errorResponse);
168168
});
169169
});
170-
});
170+
});
Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import OpenAI from 'openai';
22
import { ACI } from '../../src/client';
3-
import {
4-
ACISearchFunctions,
5-
ACIExecuteFunction
6-
} from '../../src/meta_functions';
3+
import { ACISearchFunctions, ACIExecuteFunction } from '../../src/meta_functions';
74
import { FunctionDefinitionFormat } from '../../src/types/functions';
85
// load .env.local
96
import dotenv from 'dotenv';
@@ -13,123 +10,121 @@ dotenv.config();
1310
describe('AI Integration Tests', () => {
1411
let aciClient: ACI;
1512
let openaiClient: OpenAI;
16-
13+
1714
beforeAll(() => {
1815
// Create a real API clients using environment variables
19-
aciClient = new ACI({
20-
apiKey: process.env.TEST_ACI_API_KEY as string
16+
aciClient = new ACI({
17+
apiKey: process.env.TEST_ACI_API_KEY as string,
2118
});
22-
19+
2320
openaiClient = new OpenAI({
2421
apiKey: process.env.TEST_OPENAI_API_KEY as string,
2522
});
2623
});
27-
24+
2825
// Helper to extract the first function_call from OpenAI output
2926
function getFirstFunctionCall(output: any[]) {
3027
return output.find(item => item.type === 'function_call');
3128
}
32-
29+
3330
it('completes a full AI workflow: search and execute GitHub star repo', async () => {
3431
// Get the function schemas in OpenAI format
3532
const aciSearchFunctionsSchema = ACISearchFunctions.toJsonSchema(
3633
FunctionDefinitionFormat.OPENAI_RESPONSES
3734
);
38-
35+
3936
const aciExecuteFunctionSchema = ACIExecuteFunction.toJsonSchema(
4037
FunctionDefinitionFormat.OPENAI_RESPONSES
4138
);
42-
39+
4340
// Step 1: Initial user query and function search
4441
const userMessage = 'Can you star aipotheosis-labs/aci github repo?';
45-
42+
4643
// Using as any to bypass TypeScript errors for the test
4744
const searchResponse = await openaiClient.responses.create({
4845
model: 'gpt-4o',
4946
input: userMessage,
50-
tools: [aciSearchFunctionsSchema, aciExecuteFunctionSchema] as any
47+
tools: [aciSearchFunctionsSchema, aciExecuteFunctionSchema] as any,
5148
});
52-
49+
5350
const searchToolCall = getFirstFunctionCall(searchResponse.output);
5451
expect(searchToolCall).toBeDefined();
55-
52+
5653
if (!searchToolCall || searchToolCall.type !== 'function_call') {
5754
throw new Error('No tool call returned from OpenAI');
5855
}
59-
56+
6057
// Verify it's calling the search function
6158
expect(searchToolCall.name).toBe('ACI_SEARCH_FUNCTIONS');
62-
59+
6360
// Step 2: Handle the ACI search function call
6461
const searchArguments = JSON.parse(searchToolCall.arguments);
6562
const searchResults = await aciClient.handleFunctionCall({
6663
functionName: searchToolCall.name,
6764
functionArguments: searchArguments,
6865
linkedAccountOwnerId: 'test-user-id',
6966
allowedAppsOnly: false,
70-
format: FunctionDefinitionFormat.OPENAI_RESPONSES
67+
format: FunctionDefinitionFormat.OPENAI_RESPONSES,
7168
});
72-
69+
7370
// Verify we found the GitHub star repository function
7471
expect(Array.isArray(searchResults)).toBe(true);
7572
expect(searchResults.length).toBeGreaterThan(0);
76-
73+
7774
// Find the GitHub star repository function
7875
const githubStarFunction = searchResults.find(
79-
(func: any) => func.function?.name === 'GITHUB__STAR_REPOSITORY' ||
80-
func.name === 'GITHUB__STAR_REPOSITORY'
76+
(func: any) =>
77+
func.function?.name === 'GITHUB__STAR_REPOSITORY' || func.name === 'GITHUB__STAR_REPOSITORY'
8178
);
8279
expect(githubStarFunction).toBeDefined();
83-
84-
80+
8581
// Second OpenAI call to trigger execution
8682
const executeResponse = await openaiClient.responses.create({
8783
model: 'gpt-4o',
8884
input: [
8985
{
9086
role: 'system',
9187
content:
92-
"You are a helpful assistant with access to an unlimited number of tools via some meta functions: " +
93-
"ACI_SEARCH_FUNCTIONS, and ACI_EXECUTE_FUNCTION. " +
94-
"You can use ACI_SEARCH_FUNCTIONS to find relevant functions across all apps. Try to limit the number of results per request to 1. " +
95-
"Once you have identified the function you need to use, you can use ACI_EXECUTE_FUNCTION to execute the function provided you have the correct input arguments." +
96-
"When you useing ACI_SEARCH_FUNCTIONS, the limit parameter is 10 typically"
88+
'You are a helpful assistant with access to an unlimited number of tools via some meta functions: ' +
89+
'ACI_SEARCH_FUNCTIONS, and ACI_EXECUTE_FUNCTION. ' +
90+
'You can use ACI_SEARCH_FUNCTIONS to find relevant functions across all apps. Try to limit the number of results per request to 1. ' +
91+
'Once you have identified the function you need to use, you can use ACI_EXECUTE_FUNCTION to execute the function provided you have the correct input arguments.' +
92+
'When you useing ACI_SEARCH_FUNCTIONS, the limit parameter is 10 typically',
9793
},
9894
{
9995
role: 'user',
100-
content: userMessage
96+
content: userMessage,
10197
},
10298
{
103-
...searchToolCall
99+
...searchToolCall,
104100
},
105101
{
106-
type: "function_call_output",
102+
type: 'function_call_output',
107103
call_id: searchToolCall.call_id,
108-
"output": JSON.stringify(searchResults)
109-
104+
output: JSON.stringify(searchResults),
110105
},
111106
],
112-
tools: [aciSearchFunctionsSchema, aciExecuteFunctionSchema] as any
107+
tools: [aciSearchFunctionsSchema, aciExecuteFunctionSchema] as any,
113108
} as any);
114-
109+
115110
// Extract the execution tool call
116111
const executeToolCall = getFirstFunctionCall(executeResponse.output);
117112
expect(executeToolCall).toBeDefined();
118-
113+
119114
if (!executeToolCall || executeToolCall.type !== 'function_call') {
120115
throw new Error('No execution tool call returned from OpenAI');
121116
}
122-
117+
123118
// Verify it's calling the execute function
124119
expect(executeToolCall.name).toBe('ACI_EXECUTE_FUNCTION');
125-
120+
126121
// Step 4: Check the execution parameters
127122
const executeArguments = JSON.parse(executeToolCall.arguments);
128123
expect(executeArguments.function_name).toBe('GITHUB__STAR_REPOSITORY');
129124
expect(executeArguments.function_arguments.path.owner).toBe('aipotheosis-labs');
130125
expect(executeArguments.function_arguments.path.repo).toBe('aci');
131-
126+
132127
// Note: We're not actually executing the function as it would require
133128
// real GitHub credentials, but we've verified the LLM generates the correct parameters
134129
}, 60000); // Longer timeout for this complex test
135-
});
130+
});

0 commit comments

Comments
 (0)