@theia/ai-code-completion 1.55.1 → 1.57.0-next.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/lib/browser/ai-code-completion-frontend-module.d.ts.map +1 -1
- package/lib/browser/ai-code-completion-frontend-module.js +6 -2
- package/lib/browser/ai-code-completion-frontend-module.js.map +1 -1
- package/lib/browser/ai-code-completion-preference.d.ts +3 -1
- package/lib/browser/ai-code-completion-preference.d.ts.map +1 -1
- package/lib/browser/ai-code-completion-preference.js +23 -4
- package/lib/browser/ai-code-completion-preference.js.map +1 -1
- package/lib/browser/ai-code-frontend-application-contribution.d.ts +3 -2
- package/lib/browser/ai-code-frontend-application-contribution.d.ts.map +1 -1
- package/lib/browser/ai-code-frontend-application-contribution.js +15 -3
- package/lib/browser/ai-code-frontend-application-contribution.js.map +1 -1
- package/lib/browser/ai-code-inline-completion-provider.d.ts +1 -1
- package/lib/browser/ai-code-inline-completion-provider.d.ts.map +1 -1
- package/lib/browser/ai-code-inline-completion-provider.js +1 -1
- package/lib/browser/ai-code-inline-completion-provider.js.map +1 -1
- package/lib/{common → browser}/code-completion-agent.d.ts +7 -2
- package/lib/browser/code-completion-agent.d.ts.map +1 -0
- package/lib/browser/code-completion-agent.js +211 -0
- package/lib/browser/code-completion-agent.js.map +1 -0
- package/lib/browser/code-completion-postprocessor.d.ts +11 -0
- package/lib/browser/code-completion-postprocessor.d.ts.map +1 -0
- package/lib/browser/code-completion-postprocessor.js +49 -0
- package/lib/browser/code-completion-postprocessor.js.map +1 -0
- package/lib/browser/code-completion-postprocessor.spec.d.ts +2 -0
- package/lib/browser/code-completion-postprocessor.spec.d.ts.map +1 -0
- package/lib/browser/code-completion-postprocessor.spec.js +73 -0
- package/lib/browser/code-completion-postprocessor.spec.js.map +1 -0
- package/package.json +9 -9
- package/src/browser/ai-code-completion-frontend-module.ts +7 -3
- package/src/browser/ai-code-completion-preference.ts +22 -3
- package/src/browser/ai-code-frontend-application-contribution.ts +19 -9
- package/src/browser/ai-code-inline-completion-provider.ts +1 -1
- package/src/browser/code-completion-agent.ts +233 -0
- package/src/browser/code-completion-postprocessor.spec.ts +84 -0
- package/src/browser/code-completion-postprocessor.ts +48 -0
- package/lib/common/code-completion-agent.d.ts.map +0 -1
- package/lib/common/code-completion-agent.js +0 -150
- package/lib/common/code-completion-agent.js.map +0 -1
- package/src/common/code-completion-agent.ts +0 -164
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// *****************************************************************************
|
|
3
|
+
// Copyright (C) 2024 EclipseSource GmbH.
|
|
4
|
+
//
|
|
5
|
+
// This program and the accompanying materials are made available under the
|
|
6
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
//
|
|
9
|
+
// This Source Code may also be made available under the following Secondary
|
|
10
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
// with the GNU Classpath Exception which is available at
|
|
13
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
//
|
|
15
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
// *****************************************************************************
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
const jsdom_1 = require("@theia/core/lib/browser/test/jsdom");
|
|
19
|
+
let disableJSDOM = (0, jsdom_1.enableJSDOM)();
|
|
20
|
+
const frontend_application_config_provider_1 = require("@theia/core/lib/browser/frontend-application-config-provider");
|
|
21
|
+
frontend_application_config_provider_1.FrontendApplicationConfigProvider.set({});
|
|
22
|
+
const chai_1 = require("chai");
|
|
23
|
+
const code_completion_postprocessor_1 = require("./code-completion-postprocessor");
|
|
24
|
+
disableJSDOM();
|
|
25
|
+
describe('CodeCompletionAgentImpl', () => {
|
|
26
|
+
let codeCompletionProcessor;
|
|
27
|
+
before(() => {
|
|
28
|
+
disableJSDOM = (0, jsdom_1.enableJSDOM)();
|
|
29
|
+
codeCompletionProcessor = new code_completion_postprocessor_1.DefaultCodeCompletionPostProcessor();
|
|
30
|
+
});
|
|
31
|
+
after(() => {
|
|
32
|
+
// Disable JSDOM after all tests
|
|
33
|
+
disableJSDOM();
|
|
34
|
+
});
|
|
35
|
+
describe('stripBackticks', () => {
|
|
36
|
+
it('should remove surrounding backticks and language (TypeScript)', () => {
|
|
37
|
+
const input = '```TypeScript\nconsole.log(\"Hello, World!\");```';
|
|
38
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
39
|
+
(0, chai_1.expect)(output).to.equal('console.log("Hello, World!");');
|
|
40
|
+
});
|
|
41
|
+
it('should remove surrounding backticks and language (md)', () => {
|
|
42
|
+
const input = '```md\nconsole.log(\"Hello, World!\");```';
|
|
43
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
44
|
+
(0, chai_1.expect)(output).to.equal('console.log("Hello, World!");');
|
|
45
|
+
});
|
|
46
|
+
it('should remove all text after second occurrence of backticks', () => {
|
|
47
|
+
const input = '```js\nlet x = 10;\n```\nTrailing text should be removed';
|
|
48
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
49
|
+
(0, chai_1.expect)(output).to.equal('let x = 10;');
|
|
50
|
+
});
|
|
51
|
+
it('should return the text unchanged if no surrounding backticks', () => {
|
|
52
|
+
const input = 'console.log(\"Hello, World!\");';
|
|
53
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
54
|
+
(0, chai_1.expect)(output).to.equal('console.log("Hello, World!");');
|
|
55
|
+
});
|
|
56
|
+
it('should remove surrounding backticks without language', () => {
|
|
57
|
+
const input = '```\nconsole.log(\"Hello, World!\");```';
|
|
58
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
59
|
+
(0, chai_1.expect)(output).to.equal('console.log("Hello, World!");');
|
|
60
|
+
});
|
|
61
|
+
it('should handle text starting with backticks but no second delimiter', () => {
|
|
62
|
+
const input = '```python\nprint(\"Hello, World!\")';
|
|
63
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
64
|
+
(0, chai_1.expect)(output).to.equal('print("Hello, World!")');
|
|
65
|
+
});
|
|
66
|
+
it('should handle multiple internal backticks correctly', () => {
|
|
67
|
+
const input = '```\nFoo```Bar```FooBar```';
|
|
68
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
69
|
+
(0, chai_1.expect)(output).to.equal('Foo```Bar```FooBar');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=code-completion-postprocessor.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-completion-postprocessor.spec.js","sourceRoot":"","sources":["../../src/browser/code-completion-postprocessor.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theia/ai-code-completion",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.57.0-next.112+f4778c273",
|
|
4
4
|
"description": "Theia - AI Core",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@theia/ai-core": "1.
|
|
7
|
-
"@theia/core": "1.
|
|
8
|
-
"@theia/filesystem": "1.
|
|
9
|
-
"@theia/monaco-editor-core": "1.
|
|
10
|
-
"@theia/output": "1.
|
|
11
|
-
"@theia/workspace": "1.
|
|
6
|
+
"@theia/ai-core": "1.57.0-next.112+f4778c273",
|
|
7
|
+
"@theia/core": "1.57.0-next.112+f4778c273",
|
|
8
|
+
"@theia/filesystem": "1.57.0-next.112+f4778c273",
|
|
9
|
+
"@theia/monaco-editor-core": "1.96.302",
|
|
10
|
+
"@theia/output": "1.57.0-next.112+f4778c273",
|
|
11
|
+
"@theia/workspace": "1.57.0-next.112+f4778c273",
|
|
12
12
|
"minimatch": "^5.1.0",
|
|
13
13
|
"tslib": "^2.6.2"
|
|
14
14
|
},
|
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
"watch": "theiaext watch"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@theia/ext-scripts": "1.
|
|
49
|
+
"@theia/ext-scripts": "1.58.0"
|
|
50
50
|
},
|
|
51
51
|
"nyc": {
|
|
52
52
|
"extends": "../../configs/nyc.json"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "f4778c2737bb75613f0e1f99da8996bad91f6e17"
|
|
55
55
|
}
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
|
|
17
17
|
import { ILogger } from '@theia/core';
|
|
18
18
|
import { ContainerModule } from '@theia/core/shared/inversify';
|
|
19
|
-
import { CodeCompletionAgent, CodeCompletionAgentImpl } from '
|
|
19
|
+
import { CodeCompletionAgent, CodeCompletionAgentImpl } from './code-completion-agent';
|
|
20
20
|
import { AIFrontendApplicationContribution } from './ai-code-frontend-application-contribution';
|
|
21
|
-
import { FrontendApplicationContribution, PreferenceContribution } from '@theia/core/lib/browser';
|
|
21
|
+
import { FrontendApplicationContribution, KeybindingContribution, PreferenceContribution } from '@theia/core/lib/browser';
|
|
22
22
|
import { Agent } from '@theia/ai-core';
|
|
23
23
|
import { AICodeCompletionPreferencesSchema } from './ai-code-completion-preference';
|
|
24
24
|
import { AICodeInlineCompletionsProvider } from './ai-code-inline-completion-provider';
|
|
25
|
+
import { CodeCompletionPostProcessor, DefaultCodeCompletionPostProcessor } from './code-completion-postprocessor';
|
|
25
26
|
|
|
26
27
|
export default new ContainerModule(bind => {
|
|
27
28
|
bind(ILogger).toDynamicValue(ctx => {
|
|
@@ -32,6 +33,9 @@ export default new ContainerModule(bind => {
|
|
|
32
33
|
bind(CodeCompletionAgent).toService(CodeCompletionAgentImpl);
|
|
33
34
|
bind(Agent).toService(CodeCompletionAgentImpl);
|
|
34
35
|
bind(AICodeInlineCompletionsProvider).toSelf().inSingletonScope();
|
|
35
|
-
bind(
|
|
36
|
+
bind(AIFrontendApplicationContribution).toSelf().inSingletonScope();
|
|
37
|
+
bind(FrontendApplicationContribution).to(AIFrontendApplicationContribution);
|
|
38
|
+
bind(KeybindingContribution).toService(AIFrontendApplicationContribution);
|
|
36
39
|
bind(PreferenceContribution).toConstantValue({ schema: AICodeCompletionPreferencesSchema });
|
|
40
|
+
bind(CodeCompletionPostProcessor).to(DefaultCodeCompletionPostProcessor).inSingletonScope();
|
|
37
41
|
});
|
|
@@ -17,16 +17,20 @@
|
|
|
17
17
|
import { PreferenceSchema } from '@theia/core/lib/browser/preferences/preference-contribution';
|
|
18
18
|
import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/browser/ai-core-preferences';
|
|
19
19
|
|
|
20
|
-
export const
|
|
20
|
+
export const PREF_AI_INLINE_COMPLETION_AUTOMATIC_ENABLE = 'ai-features.codeCompletion.automaticCodeCompletion';
|
|
21
21
|
export const PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS = 'ai-features.codeCompletion.excludedFileExtensions';
|
|
22
|
+
export const PREF_AI_INLINE_COMPLETION_MAX_CONTEXT_LINES = 'ai-features.codeCompletion.maxContextLines';
|
|
23
|
+
export const PREF_AI_INLINE_COMPLETION_STRIP_BACKTICKS = 'ai-features.codeCompletion.stripBackticks';
|
|
22
24
|
|
|
23
25
|
export const AICodeCompletionPreferencesSchema: PreferenceSchema = {
|
|
24
26
|
type: 'object',
|
|
25
27
|
properties: {
|
|
26
|
-
[
|
|
28
|
+
[PREF_AI_INLINE_COMPLETION_AUTOMATIC_ENABLE]: {
|
|
27
29
|
title: AI_CORE_PREFERENCES_TITLE,
|
|
28
30
|
type: 'boolean',
|
|
29
|
-
description: '
|
|
31
|
+
description: 'Automatically trigger AI completions inline within any (Monaco) editor while editing.\
|
|
32
|
+
\n\
|
|
33
|
+
Alternatively, you can manually trigger the code via the command "Trigger Inline Suggestion" or the default shortcut "Ctrl+Alt+Space".',
|
|
30
34
|
default: false
|
|
31
35
|
},
|
|
32
36
|
[PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS]: {
|
|
@@ -37,6 +41,21 @@ export const AICodeCompletionPreferencesSchema: PreferenceSchema = {
|
|
|
37
41
|
type: 'string'
|
|
38
42
|
},
|
|
39
43
|
default: []
|
|
44
|
+
},
|
|
45
|
+
[PREF_AI_INLINE_COMPLETION_MAX_CONTEXT_LINES]: {
|
|
46
|
+
title: 'Maximum Context Lines',
|
|
47
|
+
type: 'number',
|
|
48
|
+
description: 'The maximum number of lines used as context, distributed among the lines before and after the cursor position (prefix and suffix).\
|
|
49
|
+
Set this to -1 to use the full file as context without any line limit and 0 to only use the current line.',
|
|
50
|
+
default: -1,
|
|
51
|
+
minimum: -1
|
|
52
|
+
},
|
|
53
|
+
[PREF_AI_INLINE_COMPLETION_STRIP_BACKTICKS]: {
|
|
54
|
+
title: 'Strip Backticks from Inline Completions',
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
description: 'Remove surrounding backticks from the code returned by some LLMs. If a backtick is detected, all content after the closing\
|
|
57
|
+
backtick is stripped as well. This setting helps ensure plain code is returned when language models use markdown-like formatting.',
|
|
58
|
+
default: true
|
|
40
59
|
}
|
|
41
60
|
}
|
|
42
61
|
};
|
|
@@ -16,15 +16,16 @@
|
|
|
16
16
|
|
|
17
17
|
import * as monaco from '@theia/monaco-editor-core';
|
|
18
18
|
|
|
19
|
-
import { FrontendApplicationContribution, PreferenceService } from '@theia/core/lib/browser';
|
|
19
|
+
import { FrontendApplicationContribution, KeybindingContribution, KeybindingRegistry, PreferenceService } from '@theia/core/lib/browser';
|
|
20
20
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
21
21
|
import { AIActivationService } from '@theia/ai-core/lib/browser';
|
|
22
22
|
import { Disposable } from '@theia/core';
|
|
23
23
|
import { AICodeInlineCompletionsProvider } from './ai-code-inline-completion-provider';
|
|
24
|
-
import {
|
|
24
|
+
import { PREF_AI_INLINE_COMPLETION_AUTOMATIC_ENABLE, PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS } from './ai-code-completion-preference';
|
|
25
|
+
import { InlineCompletionTriggerKind } from '@theia/monaco-editor-core/esm/vs/editor/common/languages';
|
|
25
26
|
|
|
26
27
|
@injectable()
|
|
27
|
-
export class AIFrontendApplicationContribution implements FrontendApplicationContribution {
|
|
28
|
+
export class AIFrontendApplicationContribution implements FrontendApplicationContribution, KeybindingContribution {
|
|
28
29
|
@inject(AICodeInlineCompletionsProvider)
|
|
29
30
|
private inlineCodeCompletionProvider: AICodeInlineCompletionsProvider;
|
|
30
31
|
|
|
@@ -48,7 +49,8 @@ export class AIFrontendApplicationContribution implements FrontendApplicationCon
|
|
|
48
49
|
this.toDispose.set('inlineCompletions', handler());
|
|
49
50
|
|
|
50
51
|
this.preferenceService.onPreferenceChanged(event => {
|
|
51
|
-
if (event.preferenceName ===
|
|
52
|
+
if (event.preferenceName === PREF_AI_INLINE_COMPLETION_AUTOMATIC_ENABLE
|
|
53
|
+
|| event.preferenceName === PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS) {
|
|
52
54
|
this.toDispose.get('inlineCompletions')?.dispose();
|
|
53
55
|
this.toDispose.set('inlineCompletions', handler());
|
|
54
56
|
}
|
|
@@ -60,21 +62,29 @@ export class AIFrontendApplicationContribution implements FrontendApplicationCon
|
|
|
60
62
|
});
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
registerKeybindings(keybindings: KeybindingRegistry): void {
|
|
66
|
+
keybindings.registerKeybinding({
|
|
67
|
+
command: 'editor.action.inlineSuggest.trigger',
|
|
68
|
+
keybinding: 'Ctrl+Alt+Space',
|
|
69
|
+
when: '!editorReadonly && editorTextFocus'
|
|
70
|
+
});
|
|
71
|
+
}
|
|
65
72
|
|
|
66
|
-
|
|
73
|
+
protected handleInlineCompletions(): Disposable {
|
|
74
|
+
if (!this.activationService.isActive) {
|
|
67
75
|
return Disposable.NULL;
|
|
68
76
|
}
|
|
69
|
-
|
|
77
|
+
const automatic = this.preferenceService.get<boolean>(PREF_AI_INLINE_COMPLETION_AUTOMATIC_ENABLE, true);
|
|
70
78
|
const excludedExtensions = this.preferenceService.get<string[]>(PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS, []);
|
|
71
79
|
|
|
72
80
|
return monaco.languages.registerInlineCompletionsProvider(
|
|
73
81
|
{ scheme: 'file' },
|
|
74
82
|
{
|
|
75
83
|
provideInlineCompletions: (model, position, context, token) => {
|
|
84
|
+
if (!automatic && context.triggerKind === InlineCompletionTriggerKind.Automatic) {
|
|
85
|
+
return { items: [] };
|
|
86
|
+
}
|
|
76
87
|
const fileName = model.uri.toString();
|
|
77
|
-
|
|
78
88
|
if (excludedExtensions.some(ext => fileName.endsWith(ext))) {
|
|
79
89
|
return { items: [] };
|
|
80
90
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import * as monaco from '@theia/monaco-editor-core';
|
|
18
18
|
|
|
19
19
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
20
|
-
import { CodeCompletionAgent } from '
|
|
20
|
+
import { CodeCompletionAgent } from './code-completion-agent';
|
|
21
21
|
import { AgentService } from '@theia/ai-core';
|
|
22
22
|
|
|
23
23
|
@injectable()
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
Agent, AgentSpecificVariables, CommunicationRecordingService, getTextOfResponse,
|
|
19
|
+
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequirement, PromptService, PromptTemplate
|
|
20
|
+
} from '@theia/ai-core/lib/common';
|
|
21
|
+
import { generateUuid, ILogger, ProgressService } from '@theia/core';
|
|
22
|
+
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
|
23
|
+
import * as monaco from '@theia/monaco-editor-core';
|
|
24
|
+
import { PREF_AI_INLINE_COMPLETION_MAX_CONTEXT_LINES } from './ai-code-completion-preference';
|
|
25
|
+
import { PreferenceService } from '@theia/core/lib/browser';
|
|
26
|
+
import { CodeCompletionPostProcessor } from './code-completion-postprocessor';
|
|
27
|
+
|
|
28
|
+
export const CodeCompletionAgent = Symbol('CodeCompletionAgent');
|
|
29
|
+
export interface CodeCompletionAgent extends Agent {
|
|
30
|
+
provideInlineCompletions(model: monaco.editor.ITextModel, position: monaco.Position,
|
|
31
|
+
context: monaco.languages.InlineCompletionContext, token: monaco.CancellationToken): Promise<monaco.languages.InlineCompletions | undefined>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@injectable()
|
|
35
|
+
export class CodeCompletionAgentImpl implements CodeCompletionAgent {
|
|
36
|
+
async provideInlineCompletions(
|
|
37
|
+
model: monaco.editor.ITextModel,
|
|
38
|
+
position: monaco.Position,
|
|
39
|
+
context: monaco.languages.InlineCompletionContext,
|
|
40
|
+
token: monaco.CancellationToken
|
|
41
|
+
): Promise<monaco.languages.InlineCompletions | undefined> {
|
|
42
|
+
const progress = await this.progressService.showProgress(
|
|
43
|
+
{ text: 'Calculating AI code completion...', options: { location: 'window' } }
|
|
44
|
+
);
|
|
45
|
+
try {
|
|
46
|
+
const languageModel =
|
|
47
|
+
await this.languageModelRegistry.selectLanguageModel({
|
|
48
|
+
agent: this.id,
|
|
49
|
+
...this.languageModelRequirements[0],
|
|
50
|
+
});
|
|
51
|
+
if (!languageModel) {
|
|
52
|
+
this.logger.error(
|
|
53
|
+
'No language model found for code-completion-agent'
|
|
54
|
+
);
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const maxContextLines = this.preferences.get<number>(PREF_AI_INLINE_COMPLETION_MAX_CONTEXT_LINES, -1);
|
|
59
|
+
|
|
60
|
+
let prefixStartLine = 1;
|
|
61
|
+
let suffixEndLine = model.getLineCount();
|
|
62
|
+
// if maxContextLines is -1, use the full file as context without any line limit
|
|
63
|
+
|
|
64
|
+
if (maxContextLines === 0) {
|
|
65
|
+
// Only the cursor line
|
|
66
|
+
prefixStartLine = position.lineNumber;
|
|
67
|
+
suffixEndLine = position.lineNumber;
|
|
68
|
+
} else if (maxContextLines > 0) {
|
|
69
|
+
const linesBeforeCursor = position.lineNumber - 1;
|
|
70
|
+
const linesAfterCursor = model.getLineCount() - position.lineNumber;
|
|
71
|
+
|
|
72
|
+
// Allocate one more line to the prefix in case of an odd maxContextLines
|
|
73
|
+
const prefixLines = Math.min(
|
|
74
|
+
Math.ceil(maxContextLines / 2),
|
|
75
|
+
linesBeforeCursor
|
|
76
|
+
);
|
|
77
|
+
const suffixLines = Math.min(
|
|
78
|
+
Math.floor(maxContextLines / 2),
|
|
79
|
+
linesAfterCursor
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
prefixStartLine = Math.max(1, position.lineNumber - prefixLines);
|
|
83
|
+
suffixEndLine = Math.min(model.getLineCount(), position.lineNumber + suffixLines);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const prefix = model.getValueInRange({
|
|
87
|
+
startLineNumber: prefixStartLine,
|
|
88
|
+
startColumn: 1,
|
|
89
|
+
endLineNumber: position.lineNumber,
|
|
90
|
+
endColumn: position.column,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const suffix = model.getValueInRange({
|
|
94
|
+
startLineNumber: position.lineNumber,
|
|
95
|
+
startColumn: position.column,
|
|
96
|
+
endLineNumber: suffixEndLine,
|
|
97
|
+
endColumn: model.getLineMaxColumn(suffixEndLine),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const file = model.uri.toString(false);
|
|
101
|
+
const language = model.getLanguageId();
|
|
102
|
+
|
|
103
|
+
if (token.isCancellationRequested) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
const prompt = await this.promptService
|
|
107
|
+
.getPrompt('code-completion-prompt', { prefix, suffix, file, language })
|
|
108
|
+
.then(p => p?.text);
|
|
109
|
+
if (!prompt) {
|
|
110
|
+
this.logger.error('No prompt found for code-completion-agent');
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
// since we do not actually hold complete conversions, the request/response pair is considered a session
|
|
114
|
+
const sessionId = generateUuid();
|
|
115
|
+
const requestId = generateUuid();
|
|
116
|
+
const request: LanguageModelRequest = {
|
|
117
|
+
messages: [{ type: 'text', actor: 'user', query: prompt }],
|
|
118
|
+
settings: {
|
|
119
|
+
stream: false
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
if (token.isCancellationRequested) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
this.recordingService.recordRequest({
|
|
126
|
+
agentId: this.id,
|
|
127
|
+
sessionId,
|
|
128
|
+
requestId,
|
|
129
|
+
request: prompt,
|
|
130
|
+
});
|
|
131
|
+
const response = await languageModel.request(request, token);
|
|
132
|
+
if (token.isCancellationRequested) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
const completionText = await getTextOfResponse(response);
|
|
136
|
+
if (token.isCancellationRequested) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
this.recordingService.recordResponse({
|
|
140
|
+
agentId: this.id,
|
|
141
|
+
sessionId,
|
|
142
|
+
requestId,
|
|
143
|
+
response: completionText,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const postProcessedCompletionText = this.postProcessor.postProcess(completionText);
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
items: [{ insertText: postProcessedCompletionText }],
|
|
150
|
+
enableForwardStability: true,
|
|
151
|
+
};
|
|
152
|
+
} catch (e) {
|
|
153
|
+
if (!token.isCancellationRequested) {
|
|
154
|
+
console.error(e.message, e);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
progress.cancel();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@inject(ILogger)
|
|
163
|
+
@named('code-completion-agent')
|
|
164
|
+
protected logger: ILogger;
|
|
165
|
+
|
|
166
|
+
@inject(LanguageModelRegistry)
|
|
167
|
+
protected languageModelRegistry: LanguageModelRegistry;
|
|
168
|
+
|
|
169
|
+
@inject(PromptService)
|
|
170
|
+
protected promptService: PromptService;
|
|
171
|
+
|
|
172
|
+
@inject(CommunicationRecordingService)
|
|
173
|
+
protected recordingService: CommunicationRecordingService;
|
|
174
|
+
|
|
175
|
+
@inject(ProgressService)
|
|
176
|
+
protected progressService: ProgressService;
|
|
177
|
+
|
|
178
|
+
@inject(PreferenceService)
|
|
179
|
+
protected preferences: PreferenceService;
|
|
180
|
+
|
|
181
|
+
@inject(CodeCompletionPostProcessor)
|
|
182
|
+
protected postProcessor: CodeCompletionPostProcessor;
|
|
183
|
+
|
|
184
|
+
id = 'Code Completion';
|
|
185
|
+
name = 'Code Completion';
|
|
186
|
+
description =
|
|
187
|
+
'This agent provides inline code completion in the code editor in the Theia IDE.';
|
|
188
|
+
promptTemplates: PromptTemplate[] = [
|
|
189
|
+
{
|
|
190
|
+
id: 'code-completion-prompt',
|
|
191
|
+
template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here:
|
|
192
|
+
https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
|
|
193
|
+
You are a code completion agent. The current file you have to complete is named {{file}}.
|
|
194
|
+
The language of the file is {{language}}. Return your result as plain text without markdown formatting.
|
|
195
|
+
Finish the following code snippet.
|
|
196
|
+
|
|
197
|
+
{{prefix}}[[MARKER]]{{suffix}}
|
|
198
|
+
|
|
199
|
+
Only return the exact replacement for [[MARKER]] to complete the snippet.`
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
id: 'code-completion-prompt-next',
|
|
203
|
+
variantOf: 'code-completion-prompt',
|
|
204
|
+
template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here:
|
|
205
|
+
https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
|
|
206
|
+
## Code snippet
|
|
207
|
+
\`\`\`
|
|
208
|
+
{{ prefix }}[[MARKER]]{{ suffix }}
|
|
209
|
+
\`\`\`
|
|
210
|
+
|
|
211
|
+
## Meta Data
|
|
212
|
+
- File: {{file}}
|
|
213
|
+
- Language: {{language}}
|
|
214
|
+
|
|
215
|
+
Replace [[MARKER]] with the exact code to complete the code snippet. Return only the replacement of [[MAKRER]] as plain text.`,
|
|
216
|
+
},
|
|
217
|
+
];
|
|
218
|
+
languageModelRequirements: LanguageModelRequirement[] = [
|
|
219
|
+
{
|
|
220
|
+
purpose: 'code-completion',
|
|
221
|
+
identifier: 'openai/gpt-4o',
|
|
222
|
+
},
|
|
223
|
+
];
|
|
224
|
+
readonly variables: string[] = [];
|
|
225
|
+
readonly functions: string[] = [];
|
|
226
|
+
readonly agentSpecificVariables: AgentSpecificVariables[] = [
|
|
227
|
+
{ name: 'file', usedInPrompt: true, description: 'The uri of the file being edited.' },
|
|
228
|
+
{ name: 'language', usedInPrompt: true, description: 'The languageId of the file being edited.' },
|
|
229
|
+
{ name: 'prefix', usedInPrompt: true, description: 'The code before the current position of the cursor.' },
|
|
230
|
+
{ name: 'suffix', usedInPrompt: true, description: 'The code after the current position of the cursor.' }
|
|
231
|
+
];
|
|
232
|
+
readonly tags?: string[] | undefined;
|
|
233
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
|
|
18
|
+
let disableJSDOM = enableJSDOM();
|
|
19
|
+
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
|
20
|
+
FrontendApplicationConfigProvider.set({});
|
|
21
|
+
|
|
22
|
+
import { expect } from 'chai';
|
|
23
|
+
import { DefaultCodeCompletionPostProcessor } from './code-completion-postprocessor';
|
|
24
|
+
|
|
25
|
+
disableJSDOM();
|
|
26
|
+
|
|
27
|
+
describe('CodeCompletionAgentImpl', () => {
|
|
28
|
+
let codeCompletionProcessor: DefaultCodeCompletionPostProcessor;
|
|
29
|
+
before(() => {
|
|
30
|
+
disableJSDOM = enableJSDOM();
|
|
31
|
+
codeCompletionProcessor = new DefaultCodeCompletionPostProcessor();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
after(() => {
|
|
35
|
+
// Disable JSDOM after all tests
|
|
36
|
+
disableJSDOM();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('stripBackticks', () => {
|
|
40
|
+
|
|
41
|
+
it('should remove surrounding backticks and language (TypeScript)', () => {
|
|
42
|
+
const input = '```TypeScript\nconsole.log(\"Hello, World!\");```';
|
|
43
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
44
|
+
expect(output).to.equal('console.log("Hello, World!");');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should remove surrounding backticks and language (md)', () => {
|
|
48
|
+
const input = '```md\nconsole.log(\"Hello, World!\");```';
|
|
49
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
50
|
+
expect(output).to.equal('console.log("Hello, World!");');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should remove all text after second occurrence of backticks', () => {
|
|
54
|
+
const input = '```js\nlet x = 10;\n```\nTrailing text should be removed';
|
|
55
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
56
|
+
expect(output).to.equal('let x = 10;');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should return the text unchanged if no surrounding backticks', () => {
|
|
60
|
+
const input = 'console.log(\"Hello, World!\");';
|
|
61
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
62
|
+
expect(output).to.equal('console.log("Hello, World!");');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should remove surrounding backticks without language', () => {
|
|
66
|
+
const input = '```\nconsole.log(\"Hello, World!\");```';
|
|
67
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
68
|
+
expect(output).to.equal('console.log("Hello, World!");');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should handle text starting with backticks but no second delimiter', () => {
|
|
72
|
+
const input = '```python\nprint(\"Hello, World!\")';
|
|
73
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
74
|
+
expect(output).to.equal('print("Hello, World!")');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should handle multiple internal backticks correctly', () => {
|
|
78
|
+
const input = '```\nFoo```Bar```FooBar```';
|
|
79
|
+
const output = codeCompletionProcessor.stripBackticks(input);
|
|
80
|
+
expect(output).to.equal('Foo```Bar```FooBar');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
18
|
+
import { PreferenceService } from '@theia/core/lib/browser';
|
|
19
|
+
import { PREF_AI_INLINE_COMPLETION_STRIP_BACKTICKS } from './ai-code-completion-preference';
|
|
20
|
+
|
|
21
|
+
export interface CodeCompletionPostProcessor {
|
|
22
|
+
postProcess(text: string): string;
|
|
23
|
+
}
|
|
24
|
+
export const CodeCompletionPostProcessor = Symbol('CodeCompletionPostProcessor');
|
|
25
|
+
|
|
26
|
+
@injectable()
|
|
27
|
+
export class DefaultCodeCompletionPostProcessor {
|
|
28
|
+
|
|
29
|
+
@inject(PreferenceService)
|
|
30
|
+
protected readonly preferenceService: PreferenceService;
|
|
31
|
+
|
|
32
|
+
public postProcess(text: string): string {
|
|
33
|
+
if (this.preferenceService.get<boolean>(PREF_AI_INLINE_COMPLETION_STRIP_BACKTICKS, true)) {
|
|
34
|
+
return this.stripBackticks(text);
|
|
35
|
+
}
|
|
36
|
+
return text;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public stripBackticks(text: string): string {
|
|
40
|
+
if (text.startsWith('```')) {
|
|
41
|
+
// Remove the first backticks and any language identifier
|
|
42
|
+
const startRemoved = text.slice(3).replace(/^\w*\n/, '');
|
|
43
|
+
const lastBacktickIndex = startRemoved.lastIndexOf('```');
|
|
44
|
+
return lastBacktickIndex !== -1 ? startRemoved.slice(0, lastBacktickIndex).trim() : startRemoved.trim();
|
|
45
|
+
}
|
|
46
|
+
return text;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"code-completion-agent.d.ts","sourceRoot":"","sources":["../../src/common/code-completion-agent.ts"],"names":[],"mappings":"AAgBA,OAAO,EACH,KAAK,EAAE,sBAAsB,EAA6B,6BAA6B,EACvF,qBAAqB,EAAwB,wBAAwB,EAAE,aAAa,EAAE,cAAc,EACvG,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAgB,OAAO,EAAE,MAAM,aAAa,CAAC;AAEpD,OAAO,KAAK,MAAM,MAAM,2BAA2B,CAAC;AAEpD,eAAO,MAAM,mBAAmB,eAAgC,CAAC;AACjE,MAAM,WAAW,mBAAoB,SAAQ,KAAK;IAC9C,wBAAwB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAC/E,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,uBAAuB,EAAE,KAAK,EAAE,MAAM,CAAC,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAA;CACnJ;AAED,qBACa,uBAAwB,YAAW,mBAAmB;IACzD,wBAAwB,CAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,EAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,uBAAuB,EACjD,KAAK,EAAE,MAAM,CAAC,iBAAiB,GAChC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAoF1D,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAG1B,SAAS,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;IAGvD,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IAGvC,SAAS,CAAC,gBAAgB,EAAE,6BAA6B,CAAC;IAE1D,EAAE,SAAqB;IACvB,IAAI,SAAqB;IACzB,WAAW,SAC2E;IACtF,eAAe,EAAE,cAAc,EAAE,CAW/B;IACF,yBAAyB,EAAE,wBAAwB,EAAE,CAKnD;IACF,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAM;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAM;IAClC,QAAQ,CAAC,sBAAsB,EAAE,sBAAsB,EAAE,CAKvD;IACF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CACxC"}
|