Skip to content

Commit 06d8b2b

Browse files
authored
Merge pull request #2289 from giselles-ai/feat/add-ai-gateway-headers-to-github-ingest
Add stripe-customer-id header to GitHub ingest flow
2 parents 88fd701 + baed4dd commit 06d8b2b

File tree

7 files changed

+53
-25
lines changed

7 files changed

+53
-25
lines changed

apps/studio.giselles.ai/lib/vector-stores/document/ingest/ingest-document.ts

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { and, eq, lt, or } from "drizzle-orm";
1111
import { db, documentVectorStoreSources } from "@/db";
1212
import type { TeamWithSubscription } from "@/services/teams";
1313
import { fetchTeamByDbId } from "@/services/teams/fetch-team";
14+
import { buildAiGatewayHeaders } from "../../shared/ai-gateway-headers";
1415
import {
1516
deleteDocumentEmbeddingsByProfiles,
1617
getDocumentVectorStoreSource,
@@ -117,30 +118,6 @@ async function resolveEmbeddingTelemetryContext(
117118
}
118119
}
119120

120-
function buildAiGatewayHeaders(
121-
telemetryContext: DocumentEmbeddingTelemetryContext | null,
122-
): Record<string, string> | undefined {
123-
const headers: Record<string, string> = {
124-
"http-referer":
125-
process.env.AI_GATEWAY_HTTP_REFERER ?? "https://giselles.ai",
126-
"x-title": process.env.AI_GATEWAY_X_TITLE ?? "Giselle",
127-
};
128-
129-
const stripeCustomerId = telemetryContext?.team.activeCustomerId ?? undefined;
130-
const teamPlan = telemetryContext?.team.plan;
131-
if (stripeCustomerId !== undefined) {
132-
headers["stripe-customer-id"] = stripeCustomerId;
133-
headers["stripe-restricted-access-key"] =
134-
process.env.STRIPE_AI_GATEWAY_RESTRICTED_ACCESS_KEY ?? "";
135-
} else if (teamPlan === "pro" || teamPlan === "team") {
136-
console.warn(
137-
`Stripe customer ID not found for document ingest (team: ${telemetryContext?.team.id})`,
138-
);
139-
}
140-
141-
return headers;
142-
}
143-
144121
/**
145122
* Ingest a document source by extracting, chunking, and embedding text content
146123
* This function:
@@ -289,7 +266,9 @@ export async function ingestDocument(
289266
const insertedProfileIds: EmbeddingProfileId[] = [];
290267

291268
// Build AI Gateway headers for billing attribution
292-
const aiGatewayHeaders = buildAiGatewayHeaders(telemetryContext);
269+
const aiGatewayHeaders = buildAiGatewayHeaders(
270+
telemetryContext?.team ?? null,
271+
);
293272

294273
try {
295274
for (const embeddingProfileId of embeddingProfileIds) {

apps/studio.giselles.ai/lib/vector-stores/github/ingest/blobs/ingest-github-blobs.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export async function ingestGitHubBlobs(params: {
2424
teamDbId: number;
2525
embeddingProfileId: EmbeddingProfileId;
2626
embeddingComplete?: EmbeddingCompleteCallback;
27+
headers?: Record<string, string>;
2728
}): Promise<void> {
2829
const { repositoryIndexDbId, isInitialIngest } = await getRepositoryIndexInfo(
2930
params.source,
@@ -55,6 +56,7 @@ export async function ingestGitHubBlobs(params: {
5556
}),
5657
embeddingProfileId: params.embeddingProfileId,
5758
embeddingComplete: params.embeddingComplete,
59+
headers: params.headers,
5860
});
5961

6062
const result = await ingest();

apps/studio.giselles.ai/lib/vector-stores/github/ingest/issues/ingest-github-issues.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export async function ingestGitHubIssues(params: {
2121
teamDbId: number;
2222
embeddingProfileId: EmbeddingProfileId;
2323
embeddingComplete?: EmbeddingCompleteCallback;
24+
headers?: Record<string, string>;
2425
}): Promise<void> {
2526
const { repositoryIndexDbId } = await getRepositoryIndexInfo(
2627
params.source,
@@ -80,6 +81,7 @@ export async function ingestGitHubIssues(params: {
8081
},
8182
embeddingProfileId: params.embeddingProfileId,
8283
embeddingComplete: params.embeddingComplete,
84+
headers: params.headers,
8385
});
8486

8587
const result = await ingest();

apps/studio.giselles.ai/lib/vector-stores/github/ingest/process-repository.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
} from "@/db";
1616
import type { TeamWithSubscription } from "@/services/teams";
1717
import { fetchTeamByDbId } from "@/services/teams/fetch-team";
18+
import { buildAiGatewayHeaders } from "../../shared/ai-gateway-headers";
1819
import type { RepositoryWithStatuses } from "../types";
1920
import {
2021
createBlobMetadata,
@@ -76,12 +77,15 @@ const CONTENT_PROCESSORS: Record<
7677
},
7778
});
7879

80+
const headers = buildAiGatewayHeaders(team);
81+
7982
await ingestGitHubBlobs({
8083
octokitClient: octokit,
8184
source: { owner, repo, commitSha: commit.sha },
8285
teamDbId,
8386
embeddingProfileId,
8487
embeddingComplete,
88+
headers,
8589
});
8690

8791
return {
@@ -108,12 +112,15 @@ const CONTENT_PROCESSORS: Record<
108112
},
109113
});
110114

115+
const headers = buildAiGatewayHeaders(team);
116+
111117
await ingestGitHubPullRequests({
112118
githubAuthConfig: buildGitHubAuthConfig(installationId),
113119
source: { owner, repo },
114120
teamDbId,
115121
embeddingProfileId,
116122
embeddingComplete,
123+
headers,
117124
});
118125

119126
const lastPrNumber = await getLastIngestedPrNumber(
@@ -144,12 +151,15 @@ const CONTENT_PROCESSORS: Record<
144151
},
145152
});
146153

154+
const headers = buildAiGatewayHeaders(team);
155+
147156
await ingestGitHubIssues({
148157
githubAuthConfig: buildGitHubAuthConfig(installationId),
149158
source: { owner, repo },
150159
teamDbId,
151160
embeddingProfileId,
152161
embeddingComplete,
162+
headers,
153163
});
154164

155165
const lastIssueNumber = await getLastIngestedIssueNumber(

apps/studio.giselles.ai/lib/vector-stores/github/ingest/pull-requests/ingest-github-pull-requests.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export async function ingestGitHubPullRequests(params: {
2626
teamDbId: number;
2727
embeddingProfileId: EmbeddingProfileId;
2828
embeddingComplete?: EmbeddingCompleteCallback;
29+
headers?: Record<string, string>;
2930
}): Promise<void> {
3031
const { repositoryIndexDbId } = await getRepositoryIndexInfo(
3132
params.source,
@@ -59,6 +60,7 @@ export async function ingestGitHubPullRequests(params: {
5960
}),
6061
embeddingProfileId: params.embeddingProfileId,
6162
embeddingComplete: params.embeddingComplete,
63+
headers: params.headers,
6264
});
6365

6466
const result = await ingest();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { TeamWithSubscription } from "@/services/teams";
2+
3+
/**
4+
* Build AI Gateway headers for billing attribution.
5+
* This function creates headers required for AI Gateway requests,
6+
* including Stripe customer ID for usage tracking.
7+
*/
8+
export function buildAiGatewayHeaders(
9+
team: TeamWithSubscription | null,
10+
): Record<string, string> {
11+
const headers: Record<string, string> = {
12+
"http-referer":
13+
process.env.AI_GATEWAY_HTTP_REFERER ?? "https://giselles.ai",
14+
"x-title": process.env.AI_GATEWAY_X_TITLE ?? "Giselle",
15+
};
16+
17+
const stripeCustomerId = team?.activeCustomerId ?? undefined;
18+
if (stripeCustomerId !== undefined) {
19+
headers["stripe-customer-id"] = stripeCustomerId;
20+
headers["stripe-restricted-access-key"] =
21+
process.env.STRIPE_AI_GATEWAY_RESTRICTED_ACCESS_KEY ?? "";
22+
} else if (team?.plan === "pro" || team?.plan === "team") {
23+
console.warn(
24+
`Stripe customer ID not found for vector store ingest (team: ${team?.id})`,
25+
);
26+
}
27+
28+
return headers;
29+
}

packages/rag/src/ingest/pipeline.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ export interface IngestPipelineOptions<
4545
parallelLimit?: number;
4646
onProgress?: (progress: IngestProgress) => void;
4747
onError?: (error: IngestError) => void;
48+
49+
// Optional headers for AI Gateway requests
50+
headers?: Record<string, string>;
4851
}
4952

5053
const DEFAULT_MAX_BATCH_SIZE = 100;
@@ -113,6 +116,7 @@ export function createPipeline<
113116
{
114117
embeddingComplete,
115118
transport: useGateway ? "gateway" : "provider",
119+
headers: useGateway ? options.headers : undefined,
116120
},
117121
);
118122

0 commit comments

Comments
 (0)