Skip to content

Commit 5c2af60

Browse files
committed
Merge remote-tracking branch 'origin/main' into refactor/navigation
2 parents 8b9a01d + 9da5f69 commit 5c2af60

File tree

18 files changed

+486
-77
lines changed

18 files changed

+486
-77
lines changed

.github/updatecli/updatecli.d/versions.yml

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ scms:
88
user: '{{ requiredEnv "GITHUB_ACTOR" }}'
99
username: '{{ requiredEnv "GITHUB_ACTOR" }}'
1010
owner: '{{ .scm.owner }}'
11-
repository: '{{ .scm.repository }}'
11+
repository: docs-builder
1212
token: '{{ requiredEnv "GITHUB_TOKEN" }}'
1313
commitusingapi: true
1414
branch: '{{ .scm.branch }}'
@@ -47,6 +47,45 @@ sources:
4747
versionfilter:
4848
kind: latest
4949

50+
latest-edot-cf-aws-version:
51+
name: Get latest release version for the edot-cf-aws
52+
kind: githubrelease
53+
transformers:
54+
- trimprefix: v
55+
spec:
56+
owner: elastic
57+
repository: edot-cloud-forwarder-aws
58+
token: '{{ requiredEnv "GITHUB_TOKEN" }}'
59+
username: '{{ requiredEnv "GITHUB_ACTOR" }}'
60+
versionfilter:
61+
kind: latest
62+
63+
latest-edot-cf-azure-version:
64+
name: Get latest release version for the edot-cf-azure
65+
kind: githubrelease
66+
transformers:
67+
- trimprefix: v
68+
spec:
69+
owner: elastic
70+
repository: edot-cloud-forwarder-azure
71+
token: '{{ requiredEnv "GITHUB_TOKEN" }}'
72+
username: '{{ requiredEnv "GITHUB_ACTOR" }}'
73+
versionfilter:
74+
kind: latest
75+
76+
# latest-edot-cf-gcp-version:
77+
# name: Get latest release version for the edot-cf-gcp
78+
# kind: githubrelease
79+
# transformers:
80+
# - trimprefix: v
81+
# spec:
82+
# owner: elastic
83+
# repository: edot-cloud-forwarder-gcp
84+
# token: '{{ requiredEnv "GITHUB_TOKEN" }}'
85+
# username: '{{ requiredEnv "GITHUB_ACTOR" }}'
86+
# versionfilter:
87+
# kind: latest
88+
5089
latest-edot-collector-version:
5190
name: Get latest major release version for the elastic-agent
5291
kind: githubrelease
@@ -305,6 +344,33 @@ targets:
305344
file: config/versions.yml
306345
key: versioning_systems.edot-android.current
307346

347+
update-docs-docset-cf-aws:
348+
name: 'Update config/versions.yml edot-cf-aws {{ source "latest-edot-cf-aws-version" }}'
349+
scmid: githubConfig
350+
sourceid: latest-edot-cf-aws-version
351+
kind: yaml
352+
spec:
353+
file: config/versions.yml
354+
key: versioning_systems.edot-cf-aws.current
355+
356+
update-docs-docset-cf-azure:
357+
name: 'Update config/versions.yml edot-cf-azure {{ source "latest-edot-cf-azure-version" }}'
358+
scmid: githubConfig
359+
sourceid: latest-edot-cf-azure-version
360+
kind: yaml
361+
spec:
362+
file: config/versions.yml
363+
key: versioning_systems.edot-cf-azure.current
364+
365+
# update-docs-docset-cf-gcp:
366+
# name: 'Update config/versions.yml edot-cf-gcp {{ source "latest-edot-cf-gcp-version" }}'
367+
# scmid: githubConfig
368+
# sourceid: latest-edot-cf-gcp-version
369+
# kind: yaml
370+
# spec:
371+
# file: config/versions.yml
372+
# key: versioning_systems.edot-cf-gcp.current
373+
308374
update-docs-docset-collector:
309375
name: 'Update config/versions.yml edot-collector {{ source "latest-edot-collector-version" }}'
310376
scmid: githubConfig

.github/updatecli/values.d/scm.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
scm:
22
enabled: true
33
owner: elastic
4-
repository: docs-builder
4+
repositories: |
5+
docs-builder
6+
edot-cloud-forwarder-aws
7+
edot-cloud-forwarder-azure
8+
edot-cloud-forwarder-gcp
59
branch: main

.github/workflows/deploy-api-lambda.yml

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,50 @@ jobs:
5050
role-to-assume: arn:aws:iam::197730964718:role/elastic-docs-v3-api-deployer-${{ inputs.environment }}
5151
aws-region: us-east-1
5252

53-
- name: Upload Lambda function
53+
- name: Upload to S3 and Deploy Lambda Functions
5454
run: |
55-
NEW_VERSION=$(aws lambda update-function-code \
56-
--function-name "elastic-docs-v3-${ENVIRONMENT}-api" \
57-
--zip-file "fileb://${ZIP_FILE}" \
58-
--publish \
55+
# Upload once to S3 with git SHA for traceability
56+
S3_KEY="${ENVIRONMENT}/${GITHUB_SHA}/api-lambda.zip"
57+
aws s3 cp "${ZIP_FILE}" "s3://${S3_BUCKET}/${S3_KEY}"
58+
59+
# Deploy to streaming-optimized Lambda
60+
aws lambda update-function-code \
61+
--function-name "elastic-docs-v3-${ENVIRONMENT}-lambda-stream-optimized" \
62+
--s3-bucket "${S3_BUCKET}" \
63+
--s3-key "${S3_KEY}"
64+
65+
STREAM_VERSION=$(aws lambda publish-version \
66+
--function-name "elastic-docs-v3-${ENVIRONMENT}-lambda-stream-optimized" \
67+
--description "Deployed from ${GITHUB_SHA}" \
5968
--query 'Version' \
6069
--output text)
61-
70+
6271
aws lambda update-alias \
63-
--function-name "elastic-docs-v3-${ENVIRONMENT}-api" \
64-
--name live \
65-
--function-version $NEW_VERSION
72+
--function-name "elastic-docs-v3-${ENVIRONMENT}-lambda-stream-optimized" \
73+
--name live \
74+
--function-version $STREAM_VERSION
75+
76+
# Deploy to API Gateway-optimized Lambda
77+
aws lambda update-function-code \
78+
--function-name "elastic-docs-v3-${ENVIRONMENT}-lambda-api-gateway-optimized" \
79+
--s3-bucket "${S3_BUCKET}" \
80+
--s3-key "${S3_KEY}"
81+
82+
API_GW_VERSION=$(aws lambda publish-version \
83+
--function-name "elastic-docs-v3-${ENVIRONMENT}-lambda-api-gateway-optimized" \
84+
--description "Deployed from ${GITHUB_SHA}" \
85+
--query 'Version' \
86+
--output text)
87+
88+
aws lambda update-alias \
89+
--function-name "elastic-docs-v3-${ENVIRONMENT}-lambda-api-gateway-optimized" \
90+
--name live \
91+
--function-version $API_GW_VERSION
92+
93+
echo "✅ Deployed to both Lambda functions from S3: ${S3_KEY}"
94+
echo " Stream Lambda: version $STREAM_VERSION (${GITHUB_SHA})"
95+
echo " API Gateway Lambda: version $API_GW_VERSION (${GITHUB_SHA})"
6696
6797
env:
6898
ENVIRONMENT: ${{ inputs.environment }}
99+
S3_BUCKET: elastic-docs-v3-api-lambda-artifacts

.github/workflows/updatecli.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ jobs:
2828
permission-pull-requests: write
2929
repositories: |
3030
docs-builder
31+
edot-cloud-forwarder-aws
32+
edot-cloud-forwarder-azure
3133
3234
- uses: elastic/oblt-actions/updatecli/run@v1
3335
with:

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
</ItemGroup>
1313
<!-- AWS -->
1414
<ItemGroup>
15+
<PackageVersion Include="Amazon.Lambda.APIGatewayEvents" Version="2.7.1" />
1516
<PackageVersion Include="Amazon.Lambda.AspNetCoreServer.Hosting" Version="1.9.0" />
1617
<PackageVersion Include="Amazon.Lambda.RuntimeSupport" Version="1.13.4" />
1718
<PackageVersion Include="Amazon.Lambda.Core" Version="2.7.1" />

config/versions.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,11 @@ versioning_systems:
1515
# Ece version updates are manual
1616
ece:
1717
base: 4.0
18-
current: 4.0.2
18+
current: 4.0.3
1919
ech: *all
2020
eck:
2121
base: 3.0
22-
current: 3.1.0
23-
22+
current: 3.2.0
2423
ess: *all
2524
ecs:
2625
base: 9.0
@@ -102,6 +101,9 @@ versioning_systems:
102101
edot-cf-azure:
103102
base: 0.1
104103
current: 0.6.0
104+
# edot-cf-gcp:
105+
# base: 0.1
106+
# current: 0.1.0
105107

106108
# Logging
107109
ecs-logging-dotnet:

src/Elastic.Documentation.Site/Assets/web-components/SearchOrAskAi/AskAi/useFetchEventSource.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ import {
55
} from '@microsoft/fetch-event-source'
66
import { useRef, useCallback } from 'react'
77

8+
/**
9+
* Computes SHA256 hash of the request body for CloudFront + Lambda Function URL with OAC.
10+
* Required by AWS CloudFront when using Origin Access Control with Lambda Function URLs.
11+
* See: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-lambda.html
12+
*/
13+
async function computeSHA256(data: string): Promise<string> {
14+
const encoder = new TextEncoder()
15+
const dataBuffer = encoder.encode(data)
16+
const hashBuffer = await crypto.subtle.digest('SHA-256', dataBuffer)
17+
const hashArray = Array.from(new Uint8Array(hashBuffer))
18+
return hashArray.map((b) => ('0' + b.toString(16)).slice(-2)).join('')
19+
}
20+
821
// Simple wrapper interface around fetch-event-source
922
export interface UseFetchEventSourceOptions {
1023
apiEndpoint: string
@@ -47,13 +60,21 @@ export function useFetchEventSource<TPayload>({
4760
abortControllerRef.current = controller
4861

4962
try {
63+
// Stringify payload once to ensure hash matches the exact body sent
64+
const bodyString = JSON.stringify(payload)
65+
66+
// Compute SHA256 hash for CloudFront + Lambda Function URL with OAC
67+
// This proves body integrity from client to CloudFront
68+
const contentHash = await computeSHA256(bodyString)
69+
5070
await fetchEventSource(apiEndpoint, {
5171
method: 'POST',
5272
headers: {
5373
'Content-Type': 'application/json',
74+
'x-amz-content-sha256': contentHash, // Required for CloudFront OAC
5475
...headers,
5576
},
56-
body: JSON.stringify(payload),
77+
body: bodyString,
5778
signal: controller.signal, // Use local controller, not ref
5879
onopen: async (response: Response) => {
5980
if (

src/api/Elastic.Documentation.Api.Core/AskAi/AskAiUsecase.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information
44

5+
using System.Diagnostics;
56
using Microsoft.Extensions.Logging;
67

78
namespace Elastic.Documentation.Api.Core.AskAi;
@@ -11,11 +12,46 @@ public class AskAiUsecase(
1112
IStreamTransformer streamTransformer,
1213
ILogger<AskAiUsecase> logger)
1314
{
15+
private static readonly ActivitySource AskAiActivitySource = new("Elastic.Documentation.Api.AskAi");
16+
1417
public async Task<Stream> AskAi(AskAiRequest askAiRequest, Cancel ctx)
1518
{
19+
// Start activity for the chat request - DO NOT use 'using' because the stream is consumed later
20+
// The activity will be passed to the transformer which will dispose it when the stream completes
21+
var activity = AskAiActivitySource.StartActivity("chat", ActivityKind.Client);
22+
23+
// Generate a correlation ID for tracking if this is a new conversation
24+
// For first messages (no ThreadId), generate a temporary ID that will be updated when the provider responds
25+
var correlationId = askAiRequest.ThreadId ?? $"temp-{Guid.NewGuid()}";
26+
27+
// Set GenAI semantic convention attributes
28+
_ = (activity?.SetTag("gen_ai.operation.name", "chat"));
29+
_ = (activity?.SetTag("gen_ai.conversation.id", correlationId)); // Will be updated when we receive ConversationStart with actual ID
30+
_ = (activity?.SetTag("gen_ai.usage.input_tokens", askAiRequest.Message.Length)); // Approximate token count
31+
32+
// Custom attributes for tracking our abstraction layer
33+
// We use custom attributes because we don't know the actual GenAI provider (OpenAI, Anthropic, etc.)
34+
// or model (gpt-4, claude, etc.) - those are abstracted by AgentBuilder/LlmGateway
35+
_ = (activity?.SetTag("docs.ai.gateway", streamTransformer.AgentProvider)); // agent-builder or llm-gateway
36+
_ = (activity?.SetTag("docs.ai.agent_name", streamTransformer.AgentId)); // docs-agent or docs_assistant
37+
38+
// Add GenAI prompt event
39+
_ = (activity?.AddEvent(new ActivityEvent("gen_ai.content.prompt",
40+
timestamp: DateTimeOffset.UtcNow,
41+
tags:
42+
[
43+
new KeyValuePair<string, object?>("gen_ai.prompt", askAiRequest.Message),
44+
new KeyValuePair<string, object?>("gen_ai.system_instructions", AskAiRequest.SystemPrompt)
45+
])));
46+
1647
logger.LogDebug("Processing AskAiRequest: {Request}", askAiRequest);
48+
1749
var rawStream = await askAiGateway.AskAi(askAiRequest, ctx);
18-
return await streamTransformer.TransformAsync(rawStream, ctx);
50+
51+
// The stream transformer will handle disposing the activity when streaming completes
52+
var transformedStream = await streamTransformer.TransformAsync(rawStream, activity, ctx);
53+
54+
return transformedStream;
1955
}
2056
}
2157

src/api/Elastic.Documentation.Api.Core/AskAi/IStreamTransformer.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,22 @@ namespace Elastic.Documentation.Api.Core.AskAi;
99
/// </summary>
1010
public interface IStreamTransformer
1111
{
12+
/// <summary>
13+
/// Get the agent/model identifier for this transformer
14+
/// </summary>
15+
string AgentId { get; }
16+
17+
/// <summary>
18+
/// Get the agent provider/platform for this transformer
19+
/// </summary>
20+
string AgentProvider { get; }
21+
1222
/// <summary>
1323
/// Transforms a raw SSE stream into a stream of AskAiEvent objects
1424
/// </summary>
1525
/// <param name="rawStream">Raw SSE stream from gateway (Agent Builder, LLM Gateway, etc.)</param>
26+
/// <param name="parentActivity">Parent activity to track the streaming operation (will be disposed when stream completes)</param>
1627
/// <param name="cancellationToken">Cancellation token</param>
1728
/// <returns>Stream containing SSE-formatted AskAiEvent objects</returns>
18-
Task<Stream> TransformAsync(Stream rawStream, CancellationToken cancellationToken = default);
29+
Task<Stream> TransformAsync(Stream rawStream, System.Diagnostics.Activity? parentActivity, CancellationToken cancellationToken = default);
1930
}

src/api/Elastic.Documentation.Api.Infrastructure/Adapters/AskAi/AgentBuilderAskAiGateway.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ namespace Elastic.Documentation.Api.Infrastructure.Adapters.AskAi;
1515

1616
public class AgentBuilderAskAiGateway(HttpClient httpClient, IParameterProvider parameterProvider, ILogger<AgentBuilderAskAiGateway> logger) : IAskAiGateway<Stream>
1717
{
18+
/// <summary>
19+
/// Model name used by Agent Builder (from AgentId)
20+
/// </summary>
21+
public const string ModelName = "docs-agent";
22+
23+
/// <summary>
24+
/// Provider name for tracing
25+
/// </summary>
26+
public const string ProviderName = "agent-builder";
1827
public async Task<Stream> AskAi(AskAiRequest askAiRequest, Cancel ctx = default)
1928
{
2029
// Only include conversation_id if threadId is provided (subsequent requests)

0 commit comments

Comments
 (0)