Skip to content

Commit 74e76c6

Browse files
committed
github_actions
1 parent 813b417 commit 74e76c6

File tree

46 files changed

+2811
-93
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2811
-93
lines changed

core/src/oauth/providers/github.rs

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,21 @@ lazy_static! {
2424
let key = std::fs::read_to_string(path).unwrap();
2525
EncodingKey::from_rsa_pem(key.as_bytes()).unwrap()
2626
};
27+
static ref OAUTH_GITHUB_APP_PLATFORM_ACTIONS_CLIENT_ID: String =
28+
std::env::var("OAUTH_GITHUB_APP_PLATFORM_ACTIONS_CLIENT_ID").unwrap();
29+
static ref OAUTH_GITHUB_APP_PLATFORM_ACTIONS_ENCODING_KEY: EncodingKey = {
30+
let path = std::env::var("OAUTH_GITHUB_APP_PLATFORM_ACTIONS_PRIVATE_KEY_PATH").unwrap();
31+
let key = std::fs::read_to_string(path).unwrap();
32+
EncodingKey::from_rsa_pem(key.as_bytes()).unwrap()
33+
};
34+
}
35+
36+
/// We support two Github apps. Our default `connection` app (for data source connection) and a
37+
/// `platform_actions` app (for agent actions).
38+
#[derive(Debug, PartialEq, Clone)]
39+
pub enum GithubUseCase {
40+
Connection,
41+
PlatformActions,
2742
}
2843

2944
#[derive(Debug, Serialize, Deserialize)]
@@ -43,21 +58,42 @@ impl GithubConnectionProvider {
4358
// See https://docs.github.com/en
4459
// /apps/creating-github-apps/authenticating-with-a-github-app
4560
// /generating-a-json-web-token-jwt-for-a-github-app
46-
fn jwt(&self) -> Result<String> {
61+
fn jwt(&self, app_type: GithubUseCase) -> Result<String> {
4762
let header = Header::new(Algorithm::RS256);
48-
let payload = JWTPayload {
49-
iat: utils::now_secs() - 60,
50-
exp: utils::now_secs() + 3 * 60,
51-
iss: OAUTH_GITHUB_APP_CLIENT_ID.clone(),
52-
};
5363

54-
let token = encode(&header, &payload, &OAUTH_GITHUB_APP_ENCODING_KEY)?;
64+
match app_type {
65+
GithubUseCase::Connection => {
66+
let payload = JWTPayload {
67+
iat: utils::now_secs() - 60,
68+
exp: utils::now_secs() + 3 * 60,
69+
iss: OAUTH_GITHUB_APP_CLIENT_ID.clone(),
70+
};
71+
72+
let token = encode(&header, &payload, &OAUTH_GITHUB_APP_ENCODING_KEY)?;
5573

56-
Ok(token)
74+
Ok(token)
75+
}
76+
GithubUseCase::PlatformActions => {
77+
let payload = JWTPayload {
78+
iat: utils::now_secs() - 60,
79+
exp: utils::now_secs() + 3 * 60,
80+
iss: OAUTH_GITHUB_APP_PLATFORM_ACTIONS_CLIENT_ID.clone(),
81+
};
82+
83+
let token = encode(
84+
&header,
85+
&payload,
86+
&OAUTH_GITHUB_APP_PLATFORM_ACTIONS_ENCODING_KEY,
87+
)?;
88+
89+
Ok(token)
90+
}
91+
}
5792
}
5893

5994
async fn refresh_token(
6095
&self,
96+
app_type: GithubUseCase,
6197
code: &str,
6298
) -> Result<(String, u64, serde_json::Value), ProviderError> {
6399
// https://github.com/octokit/auth-app.js/blob/main/src/get-installation-authentication.ts
@@ -67,7 +103,7 @@ impl GithubConnectionProvider {
67103
code
68104
))
69105
.header("Accept", "application/vnd.github+json")
70-
.header("Authorization", format!("Bearer {}", self.jwt()?))
106+
.header("Authorization", format!("Bearer {}", self.jwt(app_type)?))
71107
.header("User-Agent", "dust/oauth")
72108
.header("X-GitHub-Api-Version", "2022-11-28");
73109

@@ -108,12 +144,21 @@ impl Provider for GithubConnectionProvider {
108144

109145
async fn finalize(
110146
&self,
111-
_connection: &Connection,
147+
connection: &Connection,
112148
code: &str,
113149
redirect_uri: &str,
114150
) -> Result<FinalizeResult, ProviderError> {
151+
let app_type = match connection.metadata()["use_case"].as_str() {
152+
Some(use_case) => match use_case {
153+
"connection" => GithubUseCase::Connection,
154+
"platform_actions" => GithubUseCase::PlatformActions,
155+
_ => Err(anyhow!("Github use_case format invalid"))?,
156+
},
157+
None => Err(anyhow!("Github use_case missing"))?,
158+
};
159+
115160
// `code` is the installation_id returned by Github.
116-
let (token, expiry, raw_json) = self.refresh_token(code).await?;
161+
let (token, expiry, raw_json) = self.refresh_token(app_type, code).await?;
117162

118163
// We store the installation_id as `code` which will be used to refresh tokens.
119164
Ok(FinalizeResult {
@@ -127,13 +172,22 @@ impl Provider for GithubConnectionProvider {
127172
}
128173

129174
async fn refresh(&self, connection: &Connection) -> Result<RefreshResult, ProviderError> {
175+
let app_type = match connection.metadata()["use_case"].as_str() {
176+
Some(use_case) => match use_case {
177+
"connection" => GithubUseCase::Connection,
178+
"platform_actions" => GithubUseCase::PlatformActions,
179+
_ => Err(anyhow!("Github use_case format invalid"))?,
180+
},
181+
None => Err(anyhow!("Github use_case missing"))?,
182+
};
183+
130184
// `code` is the installation_id returned by Github.
131185
let code = match connection.unseal_authorization_code()? {
132186
Some(code) => code,
133187
None => Err(anyhow!("Missing installation_id in connection"))?,
134188
};
135189

136-
let (token, expiry, raw_json) = self.refresh_token(&code).await?;
190+
let (token, expiry, raw_json) = self.refresh_token(app_type, &code).await?;
137191

138192
Ok(RefreshResult {
139193
access_token: token.to_string(),

front/admin/db.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
AgentDustAppRunAction,
1111
AgentDustAppRunConfiguration,
1212
} from "@app/lib/models/assistant/actions/dust_app_run";
13+
import { AgentGithubGetPullRequestAction } from "@app/lib/models/assistant/actions/github";
14+
import { AgentGithubConfiguration } from "@app/lib/models/assistant/actions/github";
1315
import {
1416
AgentProcessAction,
1517
AgentProcessConfiguration,
@@ -81,6 +83,7 @@ import {
8183
LabsTranscriptsHistoryModel,
8284
} from "@app/lib/resources/storage/models/labs_transcripts";
8385
import { MembershipModel } from "@app/lib/resources/storage/models/membership";
86+
import { PlatformActionsConfigurationModel } from "@app/lib/resources/storage/models/platform_actions";
8487
import {
8588
RunModel,
8689
RunUsageModel,
@@ -147,6 +150,7 @@ async function main() {
147150
await AgentProcessConfiguration.sync({ alter: true });
148151
await AgentWebsearchConfiguration.sync({ alter: true });
149152
await AgentBrowseConfiguration.sync({ alter: true });
153+
await AgentGithubConfiguration.sync({ alter: true });
150154

151155
await AgentDataSourceConfiguration.sync({ alter: true });
152156

@@ -166,6 +170,7 @@ async function main() {
166170
await AgentBrowseAction.sync({ alter: true });
167171
await AgentConversationIncludeFileAction.sync({ alter: true });
168172
await AgentMessageContent.sync({ alter: true });
173+
await AgentGithubGetPullRequestAction.sync({ alter: true });
169174

170175
await RetrievalDocument.sync({ alter: true });
171176
await RetrievalDocumentChunk.sync({ alter: true });
@@ -175,6 +180,8 @@ async function main() {
175180

176181
await ConversationClassification.sync({ alter: true });
177182

183+
await PlatformActionsConfigurationModel.sync({ alter: true });
184+
178185
// Labs - Can be removed at all times if a solution is dropped
179186
await LabsTranscriptsConfigurationModel.sync({ alter: true });
180187
await LabsTranscriptsHistoryModel.sync({ alter: true });

front/components/UserMenu.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
Avatar,
33
BookOpenIcon,
44
ChevronDownIcon,
5+
CloudArrowLeftRightIcon,
56
DropdownMenu,
67
DropdownMenuContent,
78
DropdownMenuItem,
@@ -100,6 +101,13 @@ export function UserMenu({
100101
href={`/w/${owner.sId}/assistant/labs/trackers`}
101102
/>
102103
)}
104+
{featureFlags.includes("labs_github_actions") && (
105+
<DropdownMenuItem
106+
label="Platform Actions"
107+
icon={CloudArrowLeftRightIcon}
108+
href={`/w/${owner.sId}/assistant/labs/platform_actions`}
109+
/>
110+
)}
103111
</>
104112
)}
105113

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { GithubIcon } from "@dust-tt/sparkle";
2+
import type { GithubGetPullRequestActionType } from "@dust-tt/types";
3+
4+
import { ActionDetailsWrapper } from "@app/components/actions/ActionDetailsWrapper";
5+
import type { ActionDetailsComponentBaseProps } from "@app/components/actions/types";
6+
7+
export function GithubGetPullRequestActionDetails({
8+
action,
9+
defaultOpen,
10+
}: ActionDetailsComponentBaseProps<GithubGetPullRequestActionType>) {
11+
const { owner, repo, pullNumber } = action.params;
12+
return (
13+
<ActionDetailsWrapper
14+
actionName="Retrieve pull request"
15+
defaultOpen={defaultOpen}
16+
visual={GithubIcon}
17+
>
18+
<div className="flex flex-col gap-4 pl-6 pt-4">
19+
<p className="text-sm font-normal text-muted-foreground">
20+
{`https://github.com/${owner}/${repo}/pull/${pullNumber}`}
21+
</p>
22+
</div>
23+
</ActionDetailsWrapper>
24+
);
25+
}

front/components/actions/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ACTION_RUNNING_LABELS } from "@dust-tt/types";
44
import { BrowseActionDetails } from "@app/components/actions/browse/BrowseActionDetails";
55
import { ConversationIncludeFileActionDetails } from "@app/components/actions/conversation/include_file/IncludeFileActionDetails";
66
import { DustAppRunActionDetails } from "@app/components/actions/dust_app_run/DustAppRunActionDetails";
7+
import { GithubGetPullRequestActionDetails } from "@app/components/actions/github/GithubGetPullRequestActionDetails";
78
import { ProcessActionDetails } from "@app/components/actions/process/ProcessActionDetails";
89
import { RetrievalActionDetails } from "@app/components/actions/retrieval/RetrievalActionDetails";
910
import { TablesQueryActionDetails } from "@app/components/actions/tables_query/TablesQueryActionDetails";
@@ -61,6 +62,10 @@ const actionsSpecification: ActionSpecifications = {
6162
detailsComponent: ConversationIncludeFileActionDetails,
6263
runningLabel: ACTION_RUNNING_LABELS.conversation_include_file_action,
6364
},
65+
github_get_pull_request_action: {
66+
detailsComponent: GithubGetPullRequestActionDetails,
67+
runningLabel: ACTION_RUNNING_LABELS.github_get_pull_request_action,
68+
},
6469
};
6570

6671
export function getActionSpecification<T extends ActionType>(

front/components/assistant/conversation/AgentMessage.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ export function AgentMessage({
247247
case "websearch_params":
248248
case "browse_params":
249249
case "conversation_include_file_params":
250+
case "github_get_pull_request_params":
250251
setStreamedAgentMessage((m) => {
251252
return updateMessageWithAction(m, event.action);
252253
});

front/components/assistant/details/AssistantActionsSection.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
CommandLineIcon,
44
ExternalLinkIcon,
55
FolderIcon,
6+
GithubIcon,
67
Icon,
78
IconButton,
89
PlanetIcon,
@@ -24,6 +25,7 @@ import {
2425
GLOBAL_AGENTS_SID,
2526
isBrowseConfiguration,
2627
isDustAppRunConfiguration,
28+
isGithubGetPullRequestConfiguration,
2729
isProcessConfiguration,
2830
isRetrievalConfiguration,
2931
isTablesQueryConfiguration,
@@ -265,13 +267,22 @@ function renderOtherAction(
265267
<Icon visual={PlanetIcon} size="sm" />
266268
<div>
267269
Assistant can navigate the web (browse any provided links, make a
268-
google search, etc.) to answer
270+
google search, etc.) to answer.
269271
</div>
270272
</div>
271273
</ActionSection>
272274
);
273275
} else if (isBrowseConfiguration(action)) {
274276
return null;
277+
} else if (isGithubGetPullRequestConfiguration(action)) {
278+
return (
279+
<ActionSection title="Github" key={`other-${index}`}>
280+
<div className="flex gap-2 text-muted-foreground">
281+
<Icon visual={GithubIcon} size="sm" />
282+
<div>Assistant can retrieve pull requests from Github.</div>
283+
</div>
284+
</ActionSection>
285+
);
275286
} else if (
276287
!isRetrievalConfiguration(action) &&
277288
!isTablesQueryConfiguration(action)

0 commit comments

Comments
 (0)