-
Notifications
You must be signed in to change notification settings - Fork 639
AI: Init, add .d.ts, add separate build, add tests #29345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
marker-dao
merged 92 commits into
DevExpress:25_1
from
marker-dao:25_1_ai_integration_init
Apr 11, 2025
Merged
Changes from all commits
Commits
Show all changes
92 commits
Select commit
Hold shift + click to select a range
09a9692
AI: Add ai-types
886913e
feat(types): Add AIProvider
92308f6
feat(ai): Add PromptManager
641f715
feat(ai): Add RequestManager
da1c8af
feat(ai): Add BaseCommand
80f97c2
feat(ai): Add AI
1fb7e4a
feat(ai): Add TranslateCommand
467715f
feat(ai): Update structure && Add AI
8ca323a
refactor(ai-types): Remove comments
5069ded
feat(ai && ai-types): Update imports
899a7a9
feat(ai): Improve typing
4140b6c
fix(ai): Fix imports
dc87270
refactor(ai-types)
d5340f8
feat(types): Add ai-types into dx.all.d.ts
99a1453
feat(ai.js)
0f48435
fix(ai.d.ts): Move types to .d.ts && Fix imports && Regenerate all
82215f8
feat && fix(ai &7 ai-types): Add implements && Fix AI type
2cc0d63
fiximport()
78284d2
test(build)
83395a6
feat(bundle): Add imports
f60a7e8
refactor
12b3162
feat(ai: tests): Add first tesrt
b7dd83a
fix && improve(tests &7 types)
ab8a654
refactor(npm.js)
520f241
refactor
6e33dfb
feat(BaseCommand: tests)
03723ec
refactor(ai.d.ts): Move AI into packaje
8c1ecea
feat(regenerate-all)
f59f3b0
feat(tests): Move AI qunit --> jest
0213f53
refactor(tests): ai
ac89bf6
refactor(jest-tests): Move files
ad2d163
feat(base command tests): Move to jest
fd0b1d8
feat && refactor(tests && templates && promptManager): Add PromptMana…
3c81359
feat(base.test): Add tests
dd57852
feat(requestManager): Add tests
1b270c3
feat(request_manager.test.ts): Add tests
04d1142
revert && feat(ai.d.ts): Move some types to ai.d.ts && Add a hidden tag
740f791
feat(ai.test.ts): Get rid of extra tests && Add new tests
9f650ec
refactor
f7d1ebe
refactor(ai.test.ts): Get rid of jest.fn
8036c52
feat(translate.test.ts): Add tests
b652d75
refactor(base.test.ts): Get rid of custom PromptManager && RequestMan…
ab0801d
fix(base.tests.ts): Remove jest.mock
caf5d6a
revert(Provider): Return Provider && Move it into separate file
678cded
feat(trnslate.test.ts): Improve tests
6fd67b6
refactor(translate.test.ts): Remove mocks
102bd80
feat(comments)
136a536
refactor(ai.tests.ts): Remove mock
7977971
refactor(request_manager.test.ts): Remove mock
df0dc4e
refactor(test_utils): Rename
29df6cb
feat(ai.d.ts): Remove hidden
d2d49d4
fix(ai.d.ts): Add publick
617ee83
skip regenerate
mpreyskurantov a0ef527
regenerate ai re-exports
mpreyskurantov d1644c1
fix(imports)
ff21a38
fix(imports)
2ec48c0
fix(renaming)
767b8de
fix(imports): Remove IAI && Add AI class to .d.ts
fcee671
revert(pg)
ae47155
fix(AI): Move AI to root && Add manual re-exports to src/index.ts && …
a203fa4
fix(ai.js): Get rif of default export
4f3042d
revert && feat(demos)
747c510
revert(.d.ts): Remove default export
d4aa58d
feat(AI): Improve AI, Base, Translate with generic type
92d69d6
revert(chat angular demo)
338b910
refactor(tests): Remove extra tests
d98051d
feat(ai): Add Command Store && Add tests
9f45f06
refactor(renaming)
4248fa6
refactor(ai.js): Remove extra lines
cdc91b6
feat(ai.d.ts): Add tags
e0c60ac
feat(AI): Improve typing
d730575
refactor(RENAMING): Rename ai to ai_integration && Fix imports
ff9d72e
refactor(wrappers && ai-folder): Remove old wrappers && Rename ai folder
0b9a0a0
refactor(RENAMING): Fix import && Rename ai.ts --> ai_integration.ts
202b7c6
refactor(RENAMING): Fix imports
cf68ab0
refactor(RENAMING): Rename AI class --> AIIntegration
a2327f1
refactor(RENAMING): Add re-exports to framework wrappers
870e737
refactor(RENAMING): Fix jest mock imports
938a189
refactor(RENAMING): Fix config.bundle files
c659f79
refactor(RENAMING): Fix import && Rename namespace
0306b5b
Rename and move js/ai_integration -> js/common/ai-integration
alexslavr 35cc0b9
Regenerate wrappers
alexslavr 0030994
Revert "skip regenerate"
alexslavr 306bd3b
Revert changes in demo systemjs configs
alexslavr 7c63660
refactor(small renaming)
46d7921
refactor(dx.ai-integration.js): Rename namespace
18b33f0
refactor
6c202de
fix(deps): Update eslint-config-devextreme to 1.1.6
16bdb17
refactor(ai-integration.d.ts): Replace interfaces with types
fb76701
refactor(ai-integration.d.ts): Rename ResponseParams to Response
5c6779c
refactor(ai-integration.d.ts): Rename promise to result && Run regene…
e5213a3
revert && refactor(.d.ts): Remove extra tags && Revert promise name
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
packages/devextreme-angular/src/common/ai-integration/index.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export { | ||
AIIntegration, | ||
AIProvider, | ||
Prompt, | ||
RequestParams, | ||
Response, | ||
} from 'devextreme/common/ai-integration'; |
5 changes: 5 additions & 0 deletions
5
packages/devextreme-angular/src/common/ai-integration/ng-package.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"lib": { | ||
"entryFile": "index.ts" | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export { | ||
AIIntegration, | ||
AIProvider, | ||
Prompt, | ||
RequestParams, | ||
Response, | ||
} from "devextreme/common/ai-integration"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export { | ||
AIIntegration, | ||
AIProvider, | ||
Prompt, | ||
RequestParams, | ||
Response, | ||
} from "devextreme/common/ai-integration"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
223 changes: 223 additions & 0 deletions
223
packages/devextreme/js/__internal/core/ai_integration/commands/base.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
import { | ||
beforeEach, | ||
describe, | ||
expect, | ||
it, | ||
jest, | ||
} from '@jest/globals'; | ||
import type { AIProvider, RequestCallbacks } from '@js/common/ai-integration'; | ||
import { BaseCommand } from '@ts/core/ai_integration/commands/base'; | ||
import type { PromptData, PromptTemplateName } from '@ts/core/ai_integration/core/prompt_manager'; | ||
import { PromptManager } from '@ts/core/ai_integration/core/prompt_manager'; | ||
import { RequestManager } from '@ts/core/ai_integration/core/request_manager'; | ||
import { Provider } from '@ts/core/ai_integration/test_utils/provider_mock'; | ||
|
||
jest.mock('@ts/core/ai_integration/templates/index', () => ({ | ||
templates: { | ||
'test-template-name': { | ||
system: 'System test template with {{first}}', | ||
user: 'User test template with {{second}}', | ||
}, | ||
}, | ||
})); | ||
|
||
interface TestCommandParams { | ||
first: string; | ||
second: string; | ||
} | ||
|
||
class TestCommand extends BaseCommand<TestCommandParams, string> { | ||
getTemplateName(): PromptTemplateName { | ||
return 'test-template-name' as PromptTemplateName; | ||
} | ||
|
||
buildPromptData(params: TestCommandParams): PromptData { | ||
const data = { | ||
system: { first: params?.first }, | ||
user: { second: params?.second }, | ||
}; | ||
|
||
return data; | ||
} | ||
|
||
parseResult(response: string): string { | ||
return `Parsed result: ${response}`; | ||
} | ||
} | ||
|
||
describe('BaseCommand', () => { | ||
let promptManager = null as unknown as PromptManager; | ||
let requestManager = null as unknown as RequestManager; | ||
let command = null as unknown as TestCommand; | ||
|
||
const params: TestCommandParams = { first: 'first', second: 'second' }; | ||
|
||
beforeEach(() => { | ||
const provider: AIProvider = new Provider(); | ||
|
||
requestManager = new RequestManager(provider); | ||
promptManager = new PromptManager(); | ||
|
||
command = new TestCommand(promptManager, requestManager); | ||
}); | ||
|
||
describe('constructor', () => { | ||
it('stores PromptManager and RequestManager correctly', () => { | ||
// @ts-expect-error Access to protected property for a test | ||
expect(command.promptManager).toBe(promptManager); | ||
// @ts-expect-error Access to protected property for a test | ||
expect(command.requestManager).toBe(requestManager); | ||
}); | ||
}); | ||
|
||
describe('execute', () => { | ||
it('getTemplateName returns value correctly', () => { | ||
const spy = jest.spyOn(command, 'getTemplateName'); | ||
|
||
command.execute(params, {}); | ||
|
||
expect(spy).toHaveBeenCalledTimes(1); | ||
expect(spy).toHaveReturnedWith('test-template-name'); | ||
}); | ||
|
||
it('buildPromptData receives and returns correct data', () => { | ||
const spy = jest.spyOn(command, 'buildPromptData'); | ||
|
||
command.execute(params, {}); | ||
|
||
expect(spy).toHaveBeenCalledTimes(1); | ||
expect(spy).toHaveBeenCalledWith(params); | ||
expect(spy).toHaveReturnedWith({ | ||
system: { first: params.first }, | ||
user: { second: params.second }, | ||
}); | ||
}); | ||
|
||
it('parseResult receives correct value and returns expected result', async () => { | ||
const spy = jest.spyOn(command, 'parseResult'); | ||
|
||
command.execute(params, {}); | ||
|
||
await new Promise(process.nextTick); | ||
|
||
expect(spy).toHaveBeenCalledTimes(1); | ||
expect(spy).toHaveBeenCalledWith('AI response'); | ||
expect(spy).toHaveReturnedWith('Parsed result: AI response'); | ||
}); | ||
|
||
it('callbacks are called correctly', async () => { | ||
const callbacks = { | ||
onComplete: jest.fn(), | ||
onError: jest.fn(), | ||
onChunk: jest.fn(), | ||
}; | ||
|
||
command.execute(params, callbacks as RequestCallbacks); | ||
|
||
await new Promise(process.nextTick); | ||
|
||
expect(callbacks.onComplete).toHaveBeenCalledTimes(1); | ||
expect(callbacks.onError).toHaveBeenCalledTimes(0); | ||
expect(callbacks.onChunk).toHaveBeenCalledTimes(2); | ||
}); | ||
|
||
it('onComplete is called with parseResult output', async () => { | ||
const callbacks = { onComplete: jest.fn() }; | ||
|
||
command.execute(params, callbacks as RequestCallbacks); | ||
|
||
await new Promise(process.nextTick); | ||
|
||
expect(callbacks.onComplete).toHaveBeenCalledWith('Parsed result: AI response'); | ||
}); | ||
|
||
it('calls onError if request fails', async () => { | ||
const originalSendRequest = requestManager.sendRequest; | ||
|
||
requestManager.sendRequest = (_, callbacks) => { | ||
callbacks.onError?.(new Error('Test error')); | ||
|
||
return (): void => {}; | ||
}; | ||
|
||
try { | ||
const callbacks = { | ||
onError: jest.fn(), | ||
onComplete: jest.fn(), | ||
}; | ||
|
||
command.execute(params, callbacks as RequestCallbacks); | ||
|
||
await new Promise(process.nextTick); | ||
|
||
expect(callbacks.onError).toHaveBeenCalledTimes(1); | ||
expect(callbacks.onError).toHaveBeenCalledWith(new Error('Test error')); | ||
expect(callbacks.onComplete).toHaveBeenCalledTimes(0); | ||
} finally { | ||
requestManager.sendRequest = originalSendRequest; | ||
} | ||
}); | ||
|
||
it('calls onChunk for each chunk and onComplete correctly', () => { | ||
const originalSendRequest = requestManager.sendRequest; | ||
|
||
requestManager.sendRequest = (_, callbacks) => { | ||
callbacks.onChunk?.('first'); | ||
callbacks.onChunk?.('second'); | ||
callbacks.onComplete?.('first second'); | ||
|
||
return (): void => {}; | ||
}; | ||
|
||
try { | ||
const onChunk = jest.fn(); | ||
const onComplete = jest.fn(); | ||
|
||
command.execute(params, { onChunk, onComplete }); | ||
|
||
expect(onChunk).toHaveBeenCalledTimes(2); | ||
expect(onChunk).toHaveBeenNthCalledWith(1, 'first'); | ||
expect(onChunk).toHaveBeenNthCalledWith(2, 'second'); | ||
expect(onComplete).toHaveBeenCalledTimes(1); | ||
expect(onComplete).toHaveBeenNthCalledWith(1, 'Parsed result: first second'); | ||
} finally { | ||
requestManager.sendRequest = originalSendRequest; | ||
} | ||
}); | ||
|
||
it('executes with undefined params without errors', async () => { | ||
const sendRequestSpy = jest.spyOn(requestManager, 'sendRequest'); | ||
const onError = jest.fn(); | ||
|
||
expect(command.execute(undefined as unknown as TestCommandParams, { onError })).not.toThrow(); | ||
|
||
await new Promise(process.nextTick); | ||
|
||
expect(onError).toHaveBeenCalledTimes(0); | ||
expect(sendRequestSpy).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('executes with partial callbacks without errors', async () => { | ||
const sendRequestSpy = jest.spyOn(requestManager, 'sendRequest'); | ||
const callbacks = { onChunk: jest.fn() }; | ||
|
||
expect(command.execute(params, callbacks)).not.toThrow(); | ||
|
||
await new Promise(process.nextTick); | ||
|
||
expect(callbacks.onChunk).toHaveBeenCalledTimes(2); | ||
expect(sendRequestSpy).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('executes with undefined callbacks without errors', () => { | ||
const sendRequestSpy = jest.spyOn(requestManager, 'sendRequest'); | ||
|
||
expect(command.execute( | ||
params, | ||
(undefined as unknown as RequestCallbacks), | ||
)).not.toThrow(); | ||
|
||
expect(sendRequestSpy).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
}); |
33 changes: 33 additions & 0 deletions
33
packages/devextreme/js/__internal/core/ai_integration/commands/base.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import type { BaseCommandResult, RequestCallbacks } from '@js/common/ai-integration'; | ||
import type { PromptData, PromptManager, PromptTemplateName } from '@ts/core/ai_integration/core/prompt_manager'; | ||
import type { RequestManager } from '@ts/core/ai_integration/core/request_manager'; | ||
|
||
export abstract class BaseCommand<TParams, TResult extends BaseCommandResult> { | ||
constructor( | ||
protected promptManager: PromptManager, | ||
protected requestManager: RequestManager, | ||
) {} | ||
|
||
public execute(params: TParams, callbacks: RequestCallbacks): () => void { | ||
const templateName = this.getTemplateName(); | ||
const data = this.buildPromptData(params); | ||
|
||
const prompt = this.promptManager.buildPrompt(templateName, data); | ||
|
||
const abort = this.requestManager.sendRequest(prompt, { | ||
onChunk: (chunk) => { callbacks?.onChunk?.(chunk); }, | ||
onComplete: (result) => { | ||
const finalResponse = this.parseResult(result); | ||
|
||
callbacks?.onComplete?.(finalResponse); | ||
}, | ||
onError: (error) => { callbacks?.onError?.(error); }, | ||
}); | ||
|
||
return abort; | ||
} | ||
|
||
protected abstract getTemplateName(): PromptTemplateName; | ||
protected abstract buildPromptData(params: TParams): PromptData; | ||
protected abstract parseResult(response: string): TResult; | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.