diff --git a/src/ghes-releases/lib/release-templates/release-steps-0.md b/src/ghes-releases/lib/release-templates/release-steps-0.md
index 0af6bcc4bb06..edce01ffb755 100644
--- a/src/ghes-releases/lib/release-templates/release-steps-0.md
+++ b/src/ghes-releases/lib/release-templates/release-steps-0.md
@@ -8,6 +8,7 @@ labels:
- skip FR board
- GHES {{ release-number }}
- ghes-release-automation
+ - rhythm of docs operations
---
This issue tracks Docs work for the GA release of GHES {{ release-number }}.
@@ -26,11 +27,11 @@ This issue tracks Docs work for the GA release of GHES {{ release-number }}.
## Instructions for triage
-- [ ] In the Enterprise project, adjust the "Cycle" field to the cycle containing the target date.
+- [ ] Add this issue to the [Rhythm of Docs: Operations](https://github.com/orgs/github/projects/20190) project.
+- [ ] For assignee: if needed, add this issue to your persona team project for tracking purposes.
-```[tasklist]
# Tasks
- [ ] {{ release-steps-1-url }}
- [ ] {{ release-steps-2-url }}
@@ -43,7 +44,6 @@ This issue tracks Docs work for the GA release of GHES {{ release-number }}.
- [ ] After merging PR for RC, notify the API team in [#ecosystem-api](https://github.slack.com/archives/C1042T6MS) on Slack that they can now merge "Update OpenAPI 3.x Descriptions" PRs in [`github/rest-api-description`](https://github.com/github/rest-api-description/pulls), which you blocked as part of the issue for preparing OpenAPI assets.
- [ ] Notify the Docs Content first responder (`@TBD`) that they can now merge OpenAPI PRs.
- [ ] To close this issue, open a PR to complete [these steps](https://github.com/github/docs-content/issues/12972#issuecomment-1947981671).
-```
## Resources
diff --git a/src/ghes-releases/lib/release-templates/release-steps-1.md b/src/ghes-releases/lib/release-templates/release-steps-1.md
index a8eae61373d0..677df370f0c1 100644
--- a/src/ghes-releases/lib/release-templates/release-steps-1.md
+++ b/src/ghes-releases/lib/release-templates/release-steps-1.md
@@ -5,12 +5,14 @@ labels:
- new-release
- priority-0
- skip FR board
+ - rhythm of docs operations
- GHES {{ release-number }}
---
## Instructions for triage
-- [ ] In the Enterprise project, adjust the "Cycle" field to the cycle containing the target date.
+- [ ] Add this issue to the [Rhythm of Docs: Operations](https://github.com/orgs/github/projects/20190) project.
+- [ ] For assignee: if needed, add this issue to your persona team project for tracking purposes.
## Instructions for assignee
diff --git a/src/ghes-releases/lib/release-templates/release-steps-2.md b/src/ghes-releases/lib/release-templates/release-steps-2.md
index 45c2c15840b7..93f61b3e4551 100644
--- a/src/ghes-releases/lib/release-templates/release-steps-2.md
+++ b/src/ghes-releases/lib/release-templates/release-steps-2.md
@@ -6,11 +6,13 @@ labels:
- priority-0
- skip FR board
- GHES {{ release-number }}
+ - rhythm of docs operations
---
## Instructions for triage
-- [ ] In the Enterprise project, adjust the "Cycle" field to the cycle **four weeks** before the target date.
+- [ ] Add this issue to the [Rhythm of Docs: Operations](https://github.com/orgs/github/projects/20190) project.
+- [ ] For assignee: if needed, add this issue to your persona team project for tracking purposes.
## Instructions for assignee
diff --git a/src/ghes-releases/lib/release-templates/release-steps-5.md b/src/ghes-releases/lib/release-templates/release-steps-5.md
index 0bc799d1e533..5ddfa696f2ba 100644
--- a/src/ghes-releases/lib/release-templates/release-steps-5.md
+++ b/src/ghes-releases/lib/release-templates/release-steps-5.md
@@ -6,11 +6,13 @@ labels:
- new-release
- priority-0
- skip FR board
+ - rhythm of docs operations
---
## Instructions for triage
-- [ ] In the Enterprise focus area's project, adjust the "Cycle" field for this issue to the cycle four weeks before the target date.
+- [ ] Add this issue to the [Rhythm of Docs: Operations](https://github.com/orgs/github/projects/20190) project.
+- [ ] For assignee: if needed, add this issue to your persona team project for tracking purposes.
## Instructions for assignee
@@ -83,7 +85,7 @@ To prepare for publication, create a PR in `github/github` that publishes the sc
- [ ] Create a PR.
-- [ ] Get the necessary reviews, but **do not deploy and merge the PR**. You'll merge this PR closer to the RC's release date, while Docs' repositories are frozen.
+- [ ] Get the necessary reviews, but **do not deploy and merge the PR yet**. You'll merge this PR closer to the RC's release date.
@@ -92,19 +94,19 @@ To prepare for publication, create a PR in `github/github` that publishes the sc
To trigger creation of an OpenAPI PR with the updated schema for the RC, merge your publication PR in `github/github`.
-- [ ] On the Thursday after you begin the freeze of Docs' repositories per {{ release-steps-0-url }}, merge the publication PR you [prepared in `github/github`](#publication-preparation).
+- [ ] 2-3 days before the RC release date, merge the publication PR you [prepared in `github/github`](#publication-preparation).
-- [ ] In [#ecosystem-api](https://github.slack.com/archives/C1042T6MS) on Slack, update and then post the following message to notify the team of the upcoming release. Replace VERSION with the version we're releasing.
+- [ ] In [#api-platform](https://github.slack.com/archives/C1042T6MS) on Slack, update and then post the following message to notify the team of the upcoming release. Replace VERSION with the version we're releasing.
> 👋 Hi from Docs! We have just marked `published` for the GHES VERSION release configuration to `true`. Please **do not** merge subsequent "Update OpenAPI 3.x Descriptions" PRs [in github/rest-api-description](https://github.com/github/rest-api-description/pulls) until further notice. Thanks!
-- [ ] On the Friday during the freeze, locate the latest "Update OpenAPI 3.x Descriptions" PRs [in `github/rest-api-description`](https://github.com/github/rest-api-description/pulls).
+- [ ] Locate the latest "Update OpenAPI 3.x Descriptions" PRs [in `github/rest-api-description`](https://github.com/github/rest-api-description/pulls).
- [ ] On each new PR, leave a review that requests changes with the following comment.
> This PR contains changes for the upcoming GHES RC. Please do not merge this or subsequent PRs until further notice in #ecosystem-api. Thanks!
-- [ ] From now until after the freeze, all [existing "Update OpenAPI Description" PRs](https://github.com/github/docs-internal/labels/github-openapi-bot) in `github/docs-internal` can be closed.
+- [ ] From now until the RC ships, all [existing "Update OpenAPI Description" PRs](https://github.com/github/docs-internal/labels/github-openapi-bot) in `github/docs-internal` can be closed.
To get the latest changes from the latest "Update OpenAPI 3.x Descriptions" PR in the `github/rest-api-description` repository into your PR to close {{ release-steps-1-url }}, you can use one of two methods.
diff --git a/src/search/lib/ai-search-proxy.ts b/src/search/lib/ai-search-proxy.ts
index 3eebf7ba3dbf..54d3d28146fb 100644
--- a/src/search/lib/ai-search-proxy.ts
+++ b/src/search/lib/ai-search-proxy.ts
@@ -1,12 +1,14 @@
import { Request, Response } from 'express'
+import statsd from '@/observability/lib/statsd'
import got from 'got'
import { getHmacWithEpoch } from '@/search/lib/helpers/get-cse-copilot-auth'
-import { getCSECopilotSource } from '#src/search/lib/helpers/cse-copilot-docs-versions.js'
+import { getCSECopilotSource } from '@/search/lib/helpers/cse-copilot-docs-versions'
const memoryCache = new Map()
export const aiSearchProxy = async (req: Request, res: Response) => {
const { query, version, language } = req.body
+
const errors = []
// Validate request body
@@ -34,13 +36,25 @@ export const aiSearchProxy = async (req: Request, res: Response) => {
return
}
+ const diagnosticTags = [
+ `version:${version}`.slice(0, 200),
+ `language:${language}`.slice(0, 200),
+ `queryLength:${query.length}`.slice(0, 200),
+ ]
+ statsd.increment('ai-search.call', 1, diagnosticTags)
+
+ // TODO: Caching here may cause an issue if the cache grows too large. Additionally, the cache will be inconsistent across pods
const cacheKey = `${query}:${version}:${language}`
if (memoryCache.has(cacheKey)) {
+ statsd.increment('ai-search.cache_hit', 1, diagnosticTags)
res.setHeader('Content-Type', 'application/x-ndjson')
res.send(memoryCache.get(cacheKey))
return
}
+ const startTime = Date.now()
+ let totalChars = 0
+
const body = {
chat_context: 'docs',
docs_source: docsSource,
@@ -57,22 +71,19 @@ export const aiSearchProxy = async (req: Request, res: Response) => {
},
})
- const chunks: Buffer[] = []
- stream.on('data', (chunk) => {
- chunks.push(chunk)
+ // Listen for data events to count characters
+ stream.on('data', (chunk: Buffer | string) => {
+ // Ensure we have a string for proper character count
+ const dataStr = typeof chunk === 'string' ? chunk : chunk.toString()
+ totalChars += dataStr.length
})
// Handle the upstream response before piping
stream.on('response', (upstreamResponse) => {
- // When cse-copilot returns a 204, it means the backend received the request
- // but was unable to answer the question. So we return a 400 to the client to be handled.
- if (upstreamResponse.statusCode === 204) {
- return res
- .status(400)
- .json({ errors: [{ message: 'Sorry I am unable to answer this question.' }] })
- } else if (upstreamResponse.statusCode !== 200) {
+ if (upstreamResponse.statusCode !== 200) {
const errorMessage = `Upstream server responded with status code ${upstreamResponse.statusCode}`
console.error(errorMessage)
+ statsd.increment('ai-search.stream_response_error', 1, diagnosticTags)
res.status(500).json({ errors: [{ message: errorMessage }] })
stream.destroy()
} else {
@@ -95,6 +106,8 @@ export const aiSearchProxy = async (req: Request, res: Response) => {
.json({ errors: [{ message: 'Sorry I am unable to answer this question.' }] })
}
+ statsd.increment('ai-search.stream_error', 1, diagnosticTags)
+
if (!res.headersSent) {
res.status(500).json({ errors: [{ message: 'Internal server error' }] })
} else {
@@ -106,12 +119,19 @@ export const aiSearchProxy = async (req: Request, res: Response) => {
}
})
- // Ensure response ends when stream ends
+ // Calculate metrics on stream end
stream.on('end', () => {
- memoryCache.set(cacheKey, Buffer.concat(chunks as Uint8Array[]))
+ const totalResponseTime = Date.now() - startTime // in ms
+ const charPerMsRatio = totalResponseTime > 0 ? totalChars / totalResponseTime : 0 // chars per ms
+
+ statsd.gauge('ai-search.total_response_time', totalResponseTime, diagnosticTags)
+ statsd.gauge('ai-search.response_chars_per_ms', charPerMsRatio, diagnosticTags)
+
+ statsd.increment('ai-search.success_stream_end', 1, diagnosticTags)
res.end()
})
} catch (error) {
+ statsd.increment('ai-search.route_error', 1, diagnosticTags)
console.error('Error posting /answers to cse-copilot:', error)
res.status(500).json({ errors: [{ message: 'Internal server error' }] })
}