Skip to content

Commit 4a1dadd

Browse files
Q1w1Nmdjastrzebski
andauthored
chore: move to byorg framework (#60)
* chore: move to byorg framework * chore: fix building and running * chore: pipeline fix * chore: fixing the pipeline * chore: brought back non-streaming response * chore: add typing for update * chore: code review changes * refactor: revert provider info naming * refactor: code review changes * chore: fix rsbuild output --------- Co-authored-by: Maciej Jastrzebski <[email protected]>
1 parent f340a88 commit 4a1dadd

32 files changed

+1866
-638
lines changed

.eslintignore

+3
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@ node_modules/
22
build/
33
dist/
44
website/
5+
rsbuild.config.ts
6+
vitest.config.ts
7+
bin.js

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
uses: ./.github/actions/setup
3636

3737
- name: Run unit tests
38-
run: yarn test --maxWorkers=2 --coverage
38+
run: yarn test --coverage
3939

4040
build-library:
4141
runs-on: ubuntu-latest

bin.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env node
2+
3+
import './dist/bin.cjs';

package.json

+15-13
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,22 @@
1313
"main": "build/index.js",
1414
"source": "src/index.js",
1515
"bin": {
16-
"ai": "build/bin.js"
16+
"ai": "./bin.js"
1717
},
1818
"files": [
19-
"build",
19+
"bin.js",
20+
"dist",
2021
"!**/__tests__",
2122
"!**/__mocks__"
2223
],
2324
"scripts": {
24-
"build": "tsc",
25-
"build:watch": "tsc --watch",
26-
"test": "jest",
25+
"build": "rsbuild build",
26+
"test": "vitest run",
2727
"typecheck": "tsc --noEmit",
2828
"lint": "eslint \"**/*.{js,ts,tsx}\"",
2929
"clean": "del-cli build",
3030
"release": "release-it",
31-
"ai": "chmod +x ./build/bin.js && ./build/bin.js"
31+
"ai": "yarn build && ./bin.js"
3232
},
3333
"repository": {
3434
"type": "git",
@@ -41,7 +41,11 @@
4141
"registry": "https://registry.npmjs.org/"
4242
},
4343
"dependencies": {
44+
"@ai-sdk/anthropic": "^0.0.54",
45+
"@ai-sdk/mistral": "^0.0.46",
46+
"@ai-sdk/openai": "^0.0.71",
4447
"@anthropic-ai/sdk": "^0.26.1",
48+
"@callstack/byorg-core": "0.1.2",
4549
"@inkjs/ui": "^1.0.0",
4650
"@mistralai/mistralai": "^1.0.2",
4751
"chalk": "^5.3.0",
@@ -50,7 +54,6 @@
5054
"ink": "^4.4.1",
5155
"ink-link": "^3.0.0",
5256
"ink-text-input": "^5.0.1",
53-
"openai": "^4.56.0",
5457
"prompts": "^2.4.2",
5558
"react": "^18.2.0",
5659
"redent": "^4.0.0",
@@ -63,32 +66,31 @@
6366
"devDependencies": {
6467
"@callstack/eslint-config": "^14.1.1",
6568
"@release-it/conventional-changelog": "^5.1.1",
69+
"@rsbuild/core": "^1.0.3",
6670
"@types/jest": "^29.5.12",
6771
"@types/mock-fs": "^4.13.4",
6872
"@types/prompts": "^2.4.9",
6973
"@types/react": "^18.2.63",
7074
"@types/update-notifier": "^6.0.8",
75+
"@vitest/coverage-v8": "^2.1.4",
7176
"del-cli": "^5.1.0",
7277
"eslint": "^8.57.0",
7378
"eslint-config-prettier": "^9.1.0",
7479
"eslint-import-resolver-typescript": "^3.6.1",
7580
"eslint-plugin-import": "^2.29.1",
7681
"eslint-plugin-prettier": "^5.1.3",
7782
"jest": "^29.7.0",
83+
"memfs": "^4.14.0",
7884
"mock-fs": "^5.2.0",
7985
"prettier": "^3.2.5",
8086
"release-it": "^15.11.0",
81-
"typescript": "^5.4.5"
87+
"typescript": "^5.4.5",
88+
"vitest": "^2.1.4"
8289
},
8390
"packageManager": "[email protected]",
8491
"engines": {
8592
"node": ">= 18.0.0"
8693
},
87-
"jest": {
88-
"modulePathIgnorePatterns": [
89-
"<rootDir>/build/"
90-
]
91-
},
9294
"prettier": {
9395
"quoteProps": "consistent",
9496
"singleQuote": true,

rsbuild.config.ts

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { defineConfig } from '@rsbuild/core';
2+
3+
export default defineConfig({
4+
dev: { progressBar: false },
5+
source: {
6+
entry: {
7+
bin: './src/bin.ts',
8+
},
9+
tsconfigPath: './tsconfig.build.json',
10+
},
11+
output: {
12+
target: 'node',
13+
minify: false,
14+
filename: {
15+
js: '[name].cjs',
16+
},
17+
externals: ['react-devtools-core'],
18+
},
19+
tools: {
20+
rspack: {
21+
module: {
22+
parser: {
23+
javascript: {
24+
dynamicImportMode: 'eager',
25+
},
26+
},
27+
},
28+
resolve: {
29+
extensionAlias: {
30+
'.js': ['.js', '.ts', '.tsx'],
31+
},
32+
},
33+
ignoreWarnings: [
34+
/require function is used in a way in which dependencies cannot be statically extracted/,
35+
/the request of a dependency is an expression/,
36+
/Can't resolve 'bufferutil'/,
37+
/Can't resolve 'utf-8-validate'/,
38+
],
39+
},
40+
},
41+
});

src/__tests__/file-utils.test.ts

+61-81
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,61 @@
1-
/* eslint-disable jest/no-commented-out-tests */
2-
it.todo('Move tests to AVA');
3-
4-
// import type { SessionContext } from '../commands/prompt/types.js';
5-
// import { DEFAULT_SYSTEM_PROMPT } from '../default-config.js';
6-
// import { getDefaultFilename, getUniqueFilename } from '../file-utils.js';
7-
// import openAi from '../engine/providers/openAi.js';
8-
9-
// const mockFs = require('mock-fs');
10-
11-
// const mockContext: SessionContext = {
12-
// config: {
13-
// model: 'gpt-4',
14-
// systemPrompt: DEFAULT_SYSTEM_PROMPT,
15-
// apiKey: '***',
16-
// },
17-
// provider: openAi,
18-
// messages: [],
19-
// totalUsage: { inputTokens: 0, outputTokens: 0, requests: 0 },
20-
// };
21-
22-
// // Allows for mocking the time on CLI
23-
// Object.defineProperty(global, 'performance', {
24-
// writable: true,
25-
// });
26-
27-
// beforeAll(() => {
28-
// jest.useFakeTimers();
29-
// jest.setSystemTime(new Date(2020, 3, 1, 12, 0));
30-
// });
31-
32-
// beforeEach(() => {
33-
// mockFs({
34-
// '/path/to/': {
35-
// 'file.txt': 'Content',
36-
// 'incr.txt': 'Content',
37-
// 'incr-1.txt': 'Content',
38-
// },
39-
// });
40-
// });
41-
42-
// // Restores the filesystem functions
43-
// afterEach(() => {
44-
// mockFs.restore();
45-
// });
46-
47-
// afterAll(() => {
48-
// jest.useRealTimers();
49-
// });
50-
51-
// describe('getDefaultFilename', () => {
52-
// it('should return the default file name', () => {
53-
// // Test case 1
54-
// const context = mockContext;
55-
// context.messages.push({ role: 'user', content: 'Hi How Are You Doing Today?' });
56-
57-
// const defaultFilename = getDefaultFilename(context);
58-
59-
// expect(defaultFilename).toBe('2020-04-01 12-00 Hi How Are You Doing');
60-
// });
61-
// });
62-
63-
// describe('getUniqueFilename', () => {
64-
// it('should return a unique file name', () => {
65-
// const filePath = '/path/to/file.txt';
66-
// const uniqueFilename = getUniqueFilename(filePath);
67-
// expect(uniqueFilename).toMatch(`/path/to/file-1.txt`);
68-
// });
69-
70-
// it('should properly increment the name', () => {
71-
// const filePath = '/path/to/incr.txt';
72-
// const uniqueFilename = getUniqueFilename(filePath);
73-
// expect(uniqueFilename).toMatch(`/path/to/incr-2.txt`);
74-
// });
75-
76-
// it('should not modify already unique name', () => {
77-
// const filePath = '/path/to/another/file.txt';
78-
// const uniqueFilename = getUniqueFilename(filePath);
79-
// expect(uniqueFilename).toMatch(`/path/to/another/file.txt`);
80-
// });
81-
// });
1+
import { Message } from '@callstack/byorg-core';
2+
import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest';
3+
import { fs, vol } from 'memfs';
4+
import { getDefaultFilename, getUniqueFilename } from '../file-utils.js';
5+
6+
vi.mock('node:fs', () => fs);
7+
8+
beforeAll(() => {
9+
vi.useFakeTimers();
10+
vi.setSystemTime(new Date(2020, 3, 1, 12, 0));
11+
return () => {
12+
vi.useRealTimers();
13+
};
14+
});
15+
16+
beforeEach(() => {
17+
vol.fromJSON(
18+
{
19+
'file.txt': 'Content',
20+
'incr.txt': 'Content',
21+
'incr-1.txt': 'Content',
22+
},
23+
'/path/to/',
24+
);
25+
26+
return () => {
27+
// // Restores the filesystem functions
28+
vol.reset();
29+
};
30+
});
31+
32+
describe('getDefaultFilename', () => {
33+
test('should return the default file name', () => {
34+
// Test case 1
35+
const messages: Message[] = [{ role: 'user', content: 'Hi How Are You Doing Today?' }];
36+
37+
const defaultFilename = getDefaultFilename(messages);
38+
39+
expect(defaultFilename).toBe('2020-04-01 12-00 Hi How Are You Doing');
40+
});
41+
});
42+
43+
describe('getUniqueFilename', () => {
44+
test('should return a unique file name', () => {
45+
const filePath = '/path/to/file.txt';
46+
const uniqueFilename = getUniqueFilename(filePath);
47+
expect(uniqueFilename).toMatch(`/path/to/file-1.txt`);
48+
});
49+
50+
test('should properly increment the name', () => {
51+
const filePath = '/path/to/incr.txt';
52+
const uniqueFilename = getUniqueFilename(filePath);
53+
expect(uniqueFilename).toMatch(`/path/to/incr-2.txt`);
54+
});
55+
56+
test('should not modify already unique name', () => {
57+
const filePath = '/path/to/another/file.txt';
58+
const uniqueFilename = getUniqueFilename(filePath);
59+
expect(uniqueFilename).toMatch(`/path/to/another/file.txt`);
60+
});
61+
});

src/__tests__/index.test.ts

-1
This file was deleted.

src/commands/chat/state/actions.ts

+16-19
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,36 @@
1-
import type { ModelResponse } from '../../../engine/inference.js';
1+
import { type AssistantMessage, type AssistantResponse } from '@callstack/byorg-core';
22
import { useChatState, type MessageLevel } from './state.js';
33

4-
export function addUserMessage(text: string) {
4+
export function addUserMessage(content: string) {
55
useChatState.setState((state) => {
66
return {
77
activeView: null,
8-
contextMessages: [...state.contextMessages, { role: 'user', content: text }],
9-
chatMessages: [...state.chatMessages, { type: 'user', text }],
8+
contextMessages: [...state.contextMessages, { role: 'user', content }],
9+
chatMessages: [...state.chatMessages, { role: 'user', content }],
1010
};
1111
});
1212
}
1313

14-
export function addAiResponse(response: ModelResponse) {
14+
export function addAssistantResponse(response: AssistantResponse) {
1515
useChatState.setState((state) => {
16-
const outputMessages = {
17-
type: 'ai',
18-
text: response.message.content,
19-
responseTime: response.responseTime,
20-
usage: response.usage,
21-
data: response.data,
22-
} as const;
16+
const message: AssistantMessage = {
17+
role: 'assistant',
18+
content: response.content,
19+
};
2320

2421
return {
2522
activeView: null,
26-
contextMessages: [...state.contextMessages, response.message],
27-
chatMessages: [...state.chatMessages, outputMessages],
23+
contextMessages: [...state.contextMessages, message],
24+
chatMessages: [...state.chatMessages, response],
2825
};
2926
});
3027
}
3128

32-
export function addProgramMessage(text: string, level: MessageLevel = 'info') {
29+
export function addProgramMessage(content: string, level: MessageLevel = 'info') {
3330
useChatState.setState((state) => {
3431
return {
3532
activeView: null,
36-
chatMessages: [...state.chatMessages, { type: 'program', level, text }],
33+
chatMessages: [...state.chatMessages, { role: 'program', level, content }],
3734
};
3835
});
3936
}
@@ -57,7 +54,7 @@ export function forgetContextMessages() {
5754
contextMessages: [],
5855
chatMessages: [
5956
...state.chatMessages,
60-
{ type: 'program', level: 'info', text: 'AI will forget previous messages.' },
57+
{ role: 'program', level: 'info', content: 'AI will forget previous messages.' },
6158
],
6259
};
6360
});
@@ -92,7 +89,7 @@ export function triggerExit() {
9289
return {
9390
activeView: null,
9491
shouldExit: true,
95-
chatMessages: [...state.chatMessages, { type: 'program', level: 'info', text: 'Bye! 👋' }],
92+
chatMessages: [...state.chatMessages, { role: 'program', level: 'info', content: 'Bye! 👋' }],
9693
};
9794
});
9895
}
@@ -103,7 +100,7 @@ export function setVerbose(verbose: boolean) {
103100
verbose,
104101
chatMessages: [
105102
...state.chatMessages,
106-
{ type: 'program', level: 'info', text: `Verbose mode: ${verbose ? 'on' : 'off'}` },
103+
{ role: 'program', level: 'info', content: `Verbose mode: ${verbose ? 'on' : 'off'}` },
107104
],
108105
};
109106
});

0 commit comments

Comments
 (0)