Skip to content

Commit c4fb2a5

Browse files
authored
Merge pull request #571 from linear-b/askAI-plugin
LINBEE-9831 -Ask ai plugin
2 parents 5fa2be4 + c1a8f9b commit c4fb2a5

File tree

9 files changed

+246
-7
lines changed

9 files changed

+246
-7
lines changed

docs/filter-function-plugins.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ description: Implement custom gitStream filter functions with JavaScript.
66

77
JavaScript plugins that enable custom filter functions for gitStream. To learn how to use these examples, read our [guide on how to use gitStream plugins](/plugins).
88

9+
askAI-plugin
10+
--8<-- "plugins/filters/askAI/README.md"
11+
912
--8<-- "plugins/filters/checklist/README.md"
1013

1114
--8<-- "plugins/filters/compareMultiSemver/README.md"
257 KB
Loading

docs/screenshots/askAI-qa.png

353 KB
Loading

plugins/filters/askAI/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 LinearB
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

plugins/filters/askAI/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
--8<-- "plugins/filters/askAI/reference.md"
3+
4+
5+
??? note "Plugin Code: askAI"
6+
```javascript
7+
--8<-- "plugins/filters/askAI/index.js"
8+
```
9+
<div class="result" markdown>
10+
<span>
11+
</span>
12+
</div>
13+
14+
15+
??? example "gitStream CM Example: askAI"
16+
```yaml+jinja
17+
--8<-- "plugins/filters/askAI/askAI.cm"
18+
```
19+
<div class="result" markdown>
20+
<span>
21+
</span>
22+
</div>
23+
24+
[Download Source Code](https://github.com/linear-b/gitstream/tree/main/plugins/filters/askAI)

plugins/filters/askAI/askAI.cm

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
triggers:
2+
exclude:
3+
branch:
4+
- r/dependabot/
5+
6+
automations:
7+
generate_pr_desc_on_new_pr:
8+
on:
9+
- pr_created
10+
if:
11+
- true
12+
run:
13+
- action: add-comment@v1
14+
args:
15+
comment: |
16+
{{ source | askAI("Based on the given context, search for new functions without tests and suggest the tests to add. If all functions are covered completely, return 'no tests to suggest.'", env.OPEN_AI_TOKEN) | encode }}
17+
18+
generate_pr_desc_on_ask_ai_label:
19+
on:
20+
- label_added
21+
if:
22+
- {{ pr.labels | match(term="/ask-ai qa") | some }}
23+
run:
24+
- action: add-comment@v1
25+
args:
26+
comment: |
27+
{{ source | askAI("Based on the given context, search for new functions without tests and suggest the tests to add. If all functions are covered completely, return 'no tests to suggest.'", env.OPEN_AI_TOKEN) | encode }}

plugins/filters/askAI/index.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/**
2+
* @module askAI
3+
* @description A gitStream plugin to interact with AI models. Currently works with `ChatGPR-4o-mini`.
4+
* @param {Object} context - The context that will be attached to the prompt .
5+
* @param {string} prompt - The prompt string.
6+
* @param {Object} token - The token to the AI model.
7+
* @returns {Object} Returns the response from the AI model.
8+
* @example {{ branch | generateDescription(pr, repo, source) }}
9+
* @license MIT
10+
**/
11+
12+
const lockFiles = [
13+
'package-lock.json',
14+
'yarn.lock',
15+
'npm-shrinkwrap.json',
16+
'Pipfile.lock',
17+
'poetry.lock',
18+
'conda-lock.yml',
19+
'Gemfile.lock',
20+
'composer.lock',
21+
'packages.lock.json',
22+
'project.assets.json',
23+
'pom.xml',
24+
'Cargo.lock',
25+
'mix.lock',
26+
'pubspec.lock',
27+
'go.sum',
28+
'stack.yaml.lock',
29+
'vcpkg.json',
30+
'conan.lock',
31+
'ivy.xml',
32+
'project.clj',
33+
'Podfile.lock',
34+
'Cartfile.resolved',
35+
'flake.lock',
36+
'pnpm-lock.yaml'
37+
];
38+
39+
const excludeExpressionsList = [
40+
'.*\\.(ini|csv|xls|xlsx|xlr|doc|docx|txt|pps|ppt|pptx|dot|dotx|log|tar|rtf|dat|ipynb|po|profile|object|obj|dxf|twb|bcsymbolmap|tfstate|pdf|rbi|pem|crt|svg|png|jpeg|jpg|ttf)$',
41+
'.*(package-lock|packages\\.lock|package)\\.json$',
42+
'.*(yarn|gemfile|podfile|cargo|composer|pipfile|gopkg)\\.lock$',
43+
'.*gradle\\.lockfile$',
44+
'.*lock\\.sbt$',
45+
'.*dist/.*\\.js',
46+
'.*public/assets/.*\\.js',
47+
'.*ci\\.yml$'
48+
];
49+
50+
const ignoreFilesRegexList = lockFiles
51+
.map(file => file.replace('.', '\\.'))
52+
.concat(excludeExpressionsList);
53+
const excludePattern = new RegExp(ignoreFilesRegexList.join('|'));
54+
55+
const filterExcludeFiles = file => {
56+
return !excludePattern.test(file) || (file.diff?.split(' ').length ?? 0) < 800;
57+
};
58+
59+
const buildArrayContext = context => {
60+
return context.filter(element => {
61+
if (typeof element !== 'object') {
62+
return true;
63+
}
64+
65+
return context.filter(filterExcludeFiles);
66+
});
67+
};
68+
69+
const buildSourceContext = context => {
70+
return context.diff.files.filter(filterExcludeFiles);
71+
};
72+
73+
const buildContextForGPT = context => {
74+
if (Array.isArray(context)) {
75+
return buildArrayContext(context);
76+
}
77+
78+
if (context?.diff?.files) {
79+
return buildSourceContext(context);
80+
}
81+
82+
return context;
83+
};
84+
85+
const askAI = async (context, prompt, token, callback) => {
86+
const cacheKey = `${__filename}${prompt}`;
87+
88+
if (process.env[cacheKey]) {
89+
return callback(null, process.env[cacheKey]);
90+
}
91+
92+
const maxTokens = 4096;
93+
const endpoint = 'https://api.openai.com/v1/chat/completions';
94+
95+
const formattedContext = buildContextForGPT(context);
96+
97+
const response = await fetch(endpoint, {
98+
method: 'POST',
99+
headers: {
100+
'Content-Type': 'application/json',
101+
Authorization: `Bearer ${token}`
102+
},
103+
body: JSON.stringify({
104+
model: 'gpt-4o-2024-08-06',
105+
messages: [
106+
{
107+
role: 'system',
108+
content: 'You are a code reviewer.'
109+
},
110+
{
111+
role: 'system',
112+
content: JSON.stringify(formattedContext)
113+
},
114+
{
115+
role: 'assistant',
116+
content:
117+
'You are code reviewer for a project. please answer without introductory phrases.'
118+
},
119+
{ role: 'user', content: prompt }
120+
],
121+
max_tokens: maxTokens
122+
})
123+
});
124+
125+
const data = await response.json();
126+
127+
const suggestion =
128+
data.choices?.[0]?.message?.content ??
129+
'context was too big for api, try with smaller context object';
130+
131+
process.env[cacheKey] = suggestion;
132+
133+
return callback(null, process.env[cacheKey]);
134+
};
135+
136+
module.exports = {
137+
async: true,
138+
filter: askAI
139+
};

plugins/filters/askAI/reference.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<a name="module_generateDescription"></a>
2+
3+
## askAI
4+
A gitStream plugin to interact with AI models. Currently works with `ChatGPR-4o-mini`
5+
6+
![Example PR description](screenshots/askAI-describe-PR.png)
7+
8+
**Returns**: <code>Object</code> - Returns the response from the AI model
9+
**License**: MIT
10+
11+
| Param | Type | Description |
12+
| ------- | -------- | ----------------------------------------------- |
13+
| context | `Object` | The context that will be attached to the prompt |
14+
| prompt | `string` | The prompt string |
15+
| token | `Object` | The token to the AI model |
16+
17+
18+
**Example**
19+
!!! tip "Encoding output"
20+
The output of AI models may be lengthy, which might cause issues when setting the comment. We recommend using the [`encode`](./filter-functions.md#encode) filter function, as shown in the example, to ensure that the comment is passed fully.
21+
The [`add-comment`](./automation-actions.md#add-comment) action automatically decodes encoded strings.
22+
23+
```yaml
24+
{{ source | askAI("Based on the given context, search for new functions without tests and suggest the tests to add. If all functions are covered completely, return 'no tests to suggest.'", env.OPEN_AI_TOKEN) | encode }}
25+
```

plugins/filters/hasJiraIssue/reference.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ Check to see if the input string matches a specified field for one or more Jira
66
**Returns**: <code>boolean</code> - Returns true if the input string matches a Jira task title.
77
**License**: MIT
88

9-
| Param | Type | Description |
10-
| --- | --- | --- |
11-
| input | <code>string</code> | The string to search for a Jira task title. |
12-
| password | <code>string</code> | Your Jira API token |
13-
| key | <code>string</code> | The Jira key to search for matches against the input string. |
14-
| jiraSpaceName | <code>string</code> | The name of the Jira space to search for tasks. |
15-
| email | <code>string</code> | The email address associated with the Jira API token. |
9+
| Param | Type | Description |
10+
| ------------- | ------------------- | ------------------------------------------------------------ |
11+
| input | <code>string</code> | The string to search for a Jira task title. |
12+
| password | <code>string</code> | Your Jira API token |
13+
| key | <code>string</code> | The Jira key to search for matches against the input string. |
14+
| jiraSpaceName | <code>string</code> | The name of the Jira space to search for tasks. |
15+
| email | <code>string</code> | The email address associated with the Jira API token. |
1616

1717
**Example**
1818
```js

0 commit comments

Comments
 (0)