Skip to content

Commit

Permalink
feat: Add AI SDK for Server-Side JavaScript. (#619)
Browse files Browse the repository at this point in the history
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: Ryan Lamb <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Todd Anderson <[email protected]>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Menelik Zafir <[email protected]>
Co-authored-by: “Menelik <“[email protected]”>
  • Loading branch information
8 people authored Nov 6, 2024
1 parent 14bb099 commit 18e8c4c
Show file tree
Hide file tree
Showing 39 changed files with 1,132 additions and 2 deletions.
1 change: 1 addition & 0 deletions .github/workflows/manual-publish-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ on:
- packages/store/node-server-sdk-dynamodb
- packages/telemetry/node-server-sdk-otel
- packages/sdk/browser
- packages/sdk/server-ai
name: Publish Documentation
jobs:
build-publish:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/manual-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ on:
- packages/telemetry/node-server-sdk-otel
- packages/tooling/jest
- packages/sdk/browser
- packages/sdk/server-ai
prerelease:
description: 'Is this a prerelease. If so, then the latest tag will not be updated in npm.'
type: boolean
Expand Down
21 changes: 21 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
package-tooling-jest-release: ${{ steps.release.outputs['packages/tooling/jest--release_created'] }}
package-react-universal-release: ${{ steps.release.outputs['packages/sdk/react-universal--release_created'] }}
package-browser-released: ${{ steps.release.outputs['packages/sdk/browser--release_created'] }}
package-server-ai-released: ${{ steps.release.outputs['packages/sdk/server-ai--release_created'] }}
steps:
- uses: googleapis/release-please-action@v4
id: release
Expand Down Expand Up @@ -377,3 +378,23 @@ jobs:
with:
workspace_path: packages/sdk/react-universal
aws_assume_role: ${{ vars.AWS_ROLE_ARN }}

release-server-ai:
runs-on: ubuntu-latest
needs: ['release-please', 'release-sdk-server']
permissions:
id-token: write
contents: write
if: ${{ always() && !failure() && !cancelled() && needs.release-please.outputs.package-server-ai-released == 'true'}}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.x
registry-url: 'https://registry.npmjs.org'
- id: release-react-native
name: Full release of packages/sdk/server-ai
uses: ./actions/full-release
with:
workspace_path: packages/sdk/server-ai
aws_assume_role: ${{ vars.AWS_ROLE_ARN }}
37 changes: 37 additions & 0 deletions .github/workflows/server-ai.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: sdk/server-ai

on:
push:
branches: [main, 'feat/**']
paths-ignore:
- '**.md' #Do not need to run CI for markdown changes.
pull_request:
branches: [main, 'feat/**']
paths-ignore:
- '**.md'

jobs:
build-test-node-server-otel:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.x
registry-url: 'https://registry.npmjs.org'
- id: shared
name: Shared CI Steps
uses: ./actions/ci
with:
workspace_name: '@launchdarkly/server-sdk-ai'
workspace_path: packages/sdk/server-ai
- name: Build bedrock example
run: |
yarn workspaces focus @launchdarkly/hello-ai-bedrock
yarn workspace @launchdarkly/hello-ai-bedrock lint
yarn workspaces foreach -pR --topological-dev --from '@launchdarkly/hello-ai-bedrock' run build
- name: Build OpenAI example
run: |
yarn workspaces focus @launchdarkly/hello-openai
yarn workspace @launchdarkly/hello-openai lint
yarn workspaces foreach -pR --topological-dev --from '@launchdarkly/hello-openai' run build
3 changes: 2 additions & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
"packages/shared/sdk-client": "1.12.0",
"packages/sdk/react-native": "10.9.2",
"packages/telemetry/node-server-sdk-otel": "1.1.1",
"packages/sdk/browser": "0.3.0"
"packages/sdk/browser": "0.3.0",
"packages/sdk/server-ai": "0.0.0"
}
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
"packages/tooling/jest/example/react-native-example",
"packages/sdk/browser",
"packages/sdk/browser/contract-tests/entity",
"packages/sdk/browser/contract-tests/adapter"
"packages/sdk/browser/contract-tests/adapter",
"packages/sdk/server-ai",
"packages/sdk/server-ai/examples/bedrock",
"packages/sdk/server-ai/examples/openai"
],
"private": true,
"scripts": {
Expand Down
68 changes: 68 additions & 0 deletions packages/sdk/server-ai/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# LaunchDarkly AI SDK for Server-Side JavaScript

# ⛔️⛔️⛔️⛔️

> [!CAUTION]
> This library is a alpha version and should not be considered ready for production use while this message is visible.
# ☝️☝️☝️☝️☝️☝️

## LaunchDarkly overview

[LaunchDarkly](https://www.launchdarkly.com) is a feature management platform that serves over 100 billion feature flags daily to help teams build better software, faster. [Get started](https://docs.launchdarkly.com/home/getting-started) using LaunchDarkly today!

[![Twitter Follow](https://img.shields.io/twitter/follow/launchdarkly.svg?style=social&label=Follow&maxAge=2592000)](https://twitter.com/intent/follow?screen_name=launchdarkly)

## Quick Setup

This assumes that you have already installed the LaunchDarkly Node.js SDK, or a compatible edge
SDK.

1. Install this package with `npm` or `yarn`:

```shell
npm install @launchdarkly/server-sdk-ai --save
```

2. Create an AI SDK instance:

```typescript
// The ldClient instance should be created based on the instructions in the relevant SDK.
const aiClient = initAi(ldClient);
```

3. Evaluate a model configuration:
```typescript
const config = await aiClient.modelConfig(
aiConfigKey!,
context,
{ enabled: false },
{ myVariable: 'My User Defined Variable' },
);
```

For an example of how to use the config please refer to the examples folder.

## Contributing

We encourage pull requests and other contributions from the community. Check out our [contributing guidelines](CONTRIBUTING.md) for instructions on how to contribute to this SDK.

## About LaunchDarkly

- LaunchDarkly is a continuous delivery platform that provides feature flags as a service and allows developers to iterate quickly and safely. We allow you to easily flag your features and manage them from the LaunchDarkly dashboard. With LaunchDarkly, you can:
- Roll out a new feature to a subset of your users (like a group of users who opt-in to a beta tester group), gathering feedback and bug reports from real-world use cases.
- Gradually roll out a feature to an increasing percentage of users, and track the effect that the feature has on key metrics (for instance, how likely is a user to complete a purchase if they have feature A versus feature B?).
- Turn off a feature that you realize is causing performance problems in production, without needing to re-deploy, or even restart the application with a changed configuration file.
- Grant access to certain features based on user attributes, like payment plan (eg: users on the ‘gold’ plan get access to more features than users in the ‘silver’ plan).
- Disable parts of your application to facilitate maintenance, without taking everything offline.
- LaunchDarkly provides feature flag SDKs for a wide variety of languages and technologies. Check out [our documentation](https://docs.launchdarkly.com/sdk) for a complete list.
- Explore LaunchDarkly
- [launchdarkly.com](https://www.launchdarkly.com/ 'LaunchDarkly Main Website') for more information
- [docs.launchdarkly.com](https://docs.launchdarkly.com/ 'LaunchDarkly Documentation') for our documentation and SDK reference guides
- [apidocs.launchdarkly.com](https://apidocs.launchdarkly.com/ 'LaunchDarkly API Documentation') for our API documentation
- [blog.launchdarkly.com](https://blog.launchdarkly.com/ 'LaunchDarkly Blog Documentation') for the latest product updates

[node-otel-ci-badge]: https://github.com/launchdarkly/js-core/actions/workflows/node-otel.yml/badge.svg
[node-otel-ci]: https://github.com/launchdarkly/js-core/actions/workflows/node-otel.yml
[node-otel-npm-badge]: https://img.shields.io/npm/v/@launchdarkly/node-server-sdk-otel.svg?style=flat-square
[node-otel-npm-link]: https://www.npmjs.com/package/@launchdarkly/node-server-sdk-otel
46 changes: 46 additions & 0 deletions packages/sdk/server-ai/examples/bedrock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# LaunchDarkly AI SDK for AWS Bedrock Example

This package demonstrates the integration of LaunchDarkly's AI SDK with AWS Bedrock, allowing you to leverage LaunchDarkly's AI Config capabilities in AI-powered applications using AWS Bedrock.

## Installation and Build

When running as part of the js-core mono-repo the project will use local dependencies.
As such those dependencies need built.

In the root of the repository run:

```bash
yarn
```

And then

```bash
yarn build
```

## Configuration

Before running the example, make sure to set the following environment variables:

- `LAUNCHDARKLY_SDK_KEY`: Your LaunchDarkly SDK key
- `LAUNCHDARKLY_AI_CONFIG_KEY`: Your LaunchDarkly AI configuration key (defaults to 'sample-ai-config' if not set)

Additionally, ensure you have proper AWS credentials configured to access Bedrock services.

## Usage

The main script (`index.js`) demonstrates how to:

1. Initialize the LaunchDarkly SDK
2. Set up a user context
3. Initialize the LaunchDarkly AI client
4. Retrieve an AI model configuration
5. Send a prompt to AWS Bedrock
6. Track token usage

To run the example (in the bedrock directory):

```bash
yarn start
```
54 changes: 54 additions & 0 deletions packages/sdk/server-ai/examples/bedrock/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "@launchdarkly/hello-ai-bedrock",
"version": "0.1.0",
"description": "LaunchDarkly AI SDK for Node.js",
"private": true,
"main": "dist/index.js",
"types": "dist/index.d.ts",
"type": "commonjs",
"scripts": {
"build": "tsc",
"start": "yarn build && node ./dist/index.js",
"lint": "npx eslint . --ext .ts",
"prettier": "prettier --write '**/*.@(js|ts|tsx|json|css)' --ignore-path ../../../.prettierignore",
"lint:fix": "yarn run lint --fix",
"check": "yarn prettier && yarn lint && yarn build && yarn test"
},
"keywords": [
"launchdarkly",
"ai",
"llm"
],
"author": "LaunchDarkly",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/client-bedrock-runtime": "^3.679.0",
"@launchdarkly/node-server-sdk": "^9.7.1",
"@launchdarkly/server-sdk-ai": "0.1.0"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
"@tsconfig/node20": "20.1.4",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
"eslint": "^8.45.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^27.6.3",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.7.0",
"prettier": "^3.0.0",
"rimraf": "^5.0.5",
"typedoc": "0.25.0",
"typescript": "^5.5.3"
},
"directories": {
"example": "example"
},
"repository": {
"type": "git",
"url": "github.com/launchdarkly/js-core"
}
}
78 changes: 78 additions & 0 deletions packages/sdk/server-ai/examples/bedrock/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/* eslint-disable no-console */
import { BedrockRuntimeClient, ConverseCommand, Message } from '@aws-sdk/client-bedrock-runtime';

import { init } from '@launchdarkly/node-server-sdk';
import { initAi } from '@launchdarkly/server-sdk-ai';

const sdkKey = process.env.LAUNCHDARKLY_SDK_KEY;
const aiConfigKey = process.env.LAUNCHDARKLY_AI_CONFIG_KEY || 'sample-ai-config';
const awsClient = new BedrockRuntimeClient({ region: 'us-east-1' });

if (!sdkKey) {
console.error('*** Please set the LAUNCHDARKLY_SDK_KEY env first');
process.exit(1);
}

if (!aiConfigKey) {
console.error('*** Please set the LAUNCHDARKLY_AI_CONFIG_KEY env first');
process.exit(1);
}

const ldClient = init(sdkKey);

// Set up the context properties
const context = {
kind: 'user',
key: 'example-user-key',
name: 'Sandy',
};

function mapPromptToConversation(
prompt: { role: 'user' | 'assistant' | 'system'; content: string }[],
): Message[] {
return prompt.map((item) => ({
// Bedrock doesn't support systems in the converse command.
role: item.role !== 'system' ? item.role : 'user',
content: [{ text: item.content }],
}));
}

async function main() {
try {
await ldClient.waitForInitialization({ timeout: 10 });
console.log('*** SDK successfully initialized');
} catch (error) {
console.log(`*** SDK failed to initialize: ${error}`);
process.exit(1);
}

const aiClient = initAi(ldClient);

const aiConfig = await aiClient.modelConfig(
aiConfigKey!,
context,
{
model: {
modelId: 'my-default-model',
},
enabled: true,
},
{
myVariable: 'My User Defined Variable',
},
);
const { tracker } = aiConfig;

const completion = tracker.trackBedrockConverse(
await awsClient.send(
new ConverseCommand({
modelId: aiConfig.config.model?.modelId ?? 'no-model',
messages: mapPromptToConversation(aiConfig.config.prompt ?? []),
}),
),
);
console.log('AI Response:', completion.output?.message?.content?.[0]?.text ?? 'no-response');
console.log('Success.');
}

main();
5 changes: 5 additions & 0 deletions packages/sdk/server-ai/examples/bedrock/tsconfig.eslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "./tsconfig.json",
"include": ["/**/*.ts", "/**/*.tsx"],
"exclude": ["node_modules"]
}
22 changes: 22 additions & 0 deletions packages/sdk/server-ai/examples/bedrock/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"extends": "@tsconfig/node20/tsconfig.json",
"compilerOptions": {
"noEmit": false,
"outDir": "dist",
"baseUrl": ".",
"allowUnusedLabels": false,
"allowUnreachableCode": false,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"sourceMap": true,
"resolveJsonModule": true,
"module": "CommonJS",
"moduleResolution": "Node"
},
"include": ["src"],
"exclude": ["dist", "node_modules"]
}
Loading

0 comments on commit 18e8c4c

Please sign in to comment.