feat: add ts

This commit is contained in:
Clelia (Astra) Bertelli
2025-07-29 14:46:31 +02:00
parent 95c132bbfb
commit 965f8a0dfa
23 changed files with 4123 additions and 14 deletions
+27
View File
@@ -0,0 +1,27 @@
name: PR check
on:
pull_request:
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "lts/*"
- name: Install dependencies
run: npm install
- name: Lint
run: npm run lint
- name: Build
run: npm run build
+53
View File
@@ -0,0 +1,53 @@
name: Release
on:
push:
branches:
- main
permissions:
contents: read # for checkout
jobs:
release:
name: Release
runs-on: ubuntu-latest
permissions:
contents: write # to be able to publish a GitHub release
issues: write # to be able to comment on released issues
pull-requests: write # to be able to comment on released pull requests
id-token: write # to enable use of OIDC for npm provenance
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "lts/*"
- name: Install dependencies
working-directory: ts/
run: npm install
- name: Lint
working-directory: ts/
run: npm run lint
- name: Build
working-directory: ts/
run: npm run build
- name: Setup npm authentication
run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Release
working-directory: ts/
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publish --access public
+1 -1
View File
@@ -18,4 +18,4 @@ jobs:
run: uv python install
- name: Run Tests
run: uv venv && source .venv/bin/activate && uv pip install pytest llama-index-core && pytest tests/test_*.py
run: uv venv && source .venv/bin/activate && uv pip install pytest llama-index-core && pytest python/tests/test_*.py
+1 -1
View File
@@ -18,5 +18,5 @@ jobs:
run: uv python install
- name: Run Mypy
working-directory: src
working-directory: python/src
run: uv venv && source .venv/bin/activate && uv pip install mypy && mypy gemini_live_demo
+4
View File
@@ -7,3 +7,7 @@ build/
# env variables
.env
# npm generated files
node_modules/
dist/
+3 -3
View File
@@ -21,7 +21,7 @@ repos:
- id: ruff
args: [--exit-non-zero-on-fix, --fix]
- id: ruff-format
exclude: ".*poetry.lock|.*_static|.*uv.lock|.*ipynb|.*docs.*"
exclude: ".*poetry.lock|.*_static|.*uv.lock|.*ipynb|.*docs.*|.*.ts"
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.0.1
@@ -44,7 +44,7 @@ repos:
--ignore-missing-imports,
--python-version=3.9,
]
entry: bash -c "export MYPYPATH=src/gemini_live_demo"
entry: bash -c "export MYPYPATH=python/src/gemini_live_demo"
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 23.10.1
@@ -52,7 +52,7 @@ repos:
- id: black-jupyter
name: black-docs-py
alias: black
files: ^(README.md|CONTRIBUTING.md)
files: ^(README.md|CONTRIBUTING.md|.*.ts)
# Using PEP 8's line length in docs prevents excess left/right scrolling
args: [--line-length=79]
+28 -5
View File
@@ -4,14 +4,14 @@ Do you want to contribute to this project? Make sure to read this guidelines fir
## Issue
**When to do it**:
### When to do it
- You found bugs but you don't know how to solve them or don't have time/will to do the solve
- You want new features but you don't know how to implement them or don't have time/will to do the implementation
> ⚠️ _Always check open and closed issues before you submit yours to avoid duplicates_
**How to do it**:
### How to do it
- Open an issue
- Give the issue a meaningful title (short but effective problem/feature request description)
@@ -19,13 +19,15 @@ Do you want to contribute to this project? Make sure to read this guidelines fir
## Traditional contribution
**When to do it**:
### When to do it
- You found bugs and corrected them
- You optimized/improved the code
- You added new features that you think could be useful to others
**How to do it**:
### How to do it
**Python**
1. Fork this repository
2. Install `pre-commit` and make sure to have it within the Git Hooks for your fork:
@@ -38,11 +40,32 @@ pre-commit install
3. Change the things you want, and make sure tests still pass or add new ones:
```bash
pytest tests/test_*.py
pytest python/tests/test_*.py
```
3. Commit your changes
4. Make sure your changes pass the pre-commit linting/type checking, if not modify them so that they pass
5. Submit pull request (make sure to provide a thorough description of the changes)
**TypeScript**
1. Fork this repository
2. Install `pre-commit` and make sure to have it within the Git Hooks for your fork:
```bash
pip install pre-commit
pre-commit install
```
3. Make changes, and make sure the package _builds_, _is linted_ and _works_:
```bash
npm run build
npm run lint
npm run start
```
4. Make sure your changes pass the pre-commit linting/type checking, if not modify them so that they pass
5. Submit pull request (make sure to provide a thorough description of the changes)
### Thanks for contributing!
+48 -3
View File
@@ -2,9 +2,11 @@
This is a demo repository showcasing Gemini Live x LlamaIndex integration.
Watch the [demo](https://www.loom.com/share/3f9b5c53d6c84fa89e7498a3ce7f1b99?sid=b87329ec-9278-439c-a626-dd1e13adbc97) for a quick overview!
Watch the [python](https://www.loom.com/share/3f9b5c53d6c84fa89e7498a3ce7f1b99?sid=b87329ec-9278-439c-a626-dd1e13adbc97) and the [typescript]() demo videos for a quick overview!
## Install and Launch
## Python
### Install and Launch
> [!IMPORTANT]
>
@@ -14,7 +16,7 @@ Clone this repository locally:
```bash
git clone https://github.com/run-llama/gemini-live-demo
cd gemini-live-demo
cd gemini-live-demo/python
```
And install the needed dependencies:
@@ -36,6 +38,49 @@ Launch the application with
uv run src/gemini_live_demo/main.py
```
## TypeScript
### Install and Launch
Make sure to export your `GOOGLE_API_KEY` before running the demo.
```bash
export GOOGLE_API_KEY="my-google-api-key"
```
**User Set-Up**
```bash
npx @cle-does-things/live-chat
```
**Developer Set-Up**
Clone this repository locally:
```bash
git clone https://github.com/run-llama/gemini-live-demo
cd gemini-live-demo/ts
```
And install the needed dependencies:
```bash
npm install
```
Build the package:
```bash
npm run build
```
Run the package:
```bash
npm run start
```
## Contributing
We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) to get started.
@@ -13,7 +13,10 @@ def messages() -> List[ChatMessage]:
ChatMessage(role="user", blocks=[AudioBlock(audio=b"Hello")]),
ChatMessage(
role="assistant",
blocks=[AudioBlock(audio=b"Hello back"), TextBlock(text="hello back")],
blocks=[
AudioBlock(audio=b"Hello back"),
TextBlock(text="hello back"),
],
),
ChatMessage(role="user", content="now what?"),
]
View File
+15
View File
@@ -0,0 +1,15 @@
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import { defineConfig } from "eslint/config";
export default defineConfig([
{
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
plugins: { js },
extends: ["js/recommended"],
languageOptions: { globals: globals.browser },
},
{ files: ["**/*.js"], languageOptions: { sourceType: "commonjs" } },
tseslint.configs.recommended,
]);
+3732
View File
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
{
"name": "@cle-does-things/live-chat",
"version": "0.1.0",
"description": "Demo for Gemini Live and LlamaIndexTS",
"keywords": [
"ai",
"voice",
"gemini",
"live",
"demo",
"llm"
],
"homepage": "https://github.com/run-llama/gemini-live-demo#readme",
"bugs": {
"url": "https://github.com/run-llama/gemini-live-demo/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/run-llama/gemini-live-demo.git"
},
"license": "MIT",
"author": "Clelia Astra Bertelli",
"type": "commonjs",
"main": "dist/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "npx tsx src/index.ts",
"lint": "eslint src/",
"build": "tsc",
"build:watch": "tsc --watch",
"dev": "npx tsx --watch src/index.ts",
"prod": "npm run build && node dist/index.js"
},
"dependencies": {
"@llamaindex/google": "^0.3.17",
"consola": "^3.4.2",
"oh-my-logo": "^0.2.1",
"picocolors": "1.0.1",
"speaker": "^0.5.5",
"tsx": "^4.20.3"
},
"devDependencies": {
"@eslint/js": "^9.32.0",
"@types/node": "^20.0.0",
"@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^8.38.0",
"eslint": "^9.32.0",
"globals": "^16.3.0",
"jiti": "^2.5.1",
"typescript": "^5.0.0",
"typescript-eslint": "^8.38.0"
}
}
+57
View File
@@ -0,0 +1,57 @@
import { gemini, GEMINI_MODEL } from "@llamaindex/google";
import { outputAudio, decodePCMData, consoleInput, renderLogo } from "./utils";
import { logger } from "./logger";
import pc from "picocolors";
export async function main(): Promise<number> {
// Server-side: Generate ephemeral key
const serverLlm = gemini({
model: GEMINI_MODEL.GEMINI_2_0_FLASH_LIVE,
httpOptions: { apiVersion: "v1alpha" },
});
const ephemeralKey = await serverLlm.live.getEphemeralKey();
// Client-side: Use ephemeral key for Live API
const llm = gemini({
apiKey: ephemeralKey,
model: GEMINI_MODEL.GEMINI_2_0_FLASH_LIVE,
voiceName: "Zephyr",
httpOptions: { apiVersion: "v1alpha" },
});
const session = await llm.live.connect();
await renderLogo();
logger.log(
`Welcome to ${pc.bold(pc.cyan("✨Live Chat✨"))}, our demo for ${pc.bold(
pc.magenta("Gemini Live🎙️"),
)} and ${pc.bold(
pc.magenta("LlamaIndexTS🦙"),
)}.\nWrite messages to Gemini Live and wait for its answer in the chat below.\nIf you wish to exit, just type ${pc.bold(
pc.gray("quit"),
)}.\n`,
);
while (true) {
const audioBuffers: Buffer[] = [];
const userInput = await consoleInput();
if (userInput == "quit") {
break;
}
session.sendMessage({
role: "user",
content: userInput,
});
for await (const event of session.streamEvents()) {
if (event.type == "audio") {
audioBuffers.push(decodePCMData(event.data));
} else if (event.type == "text") {
logger.log(pc.bold(pc.magenta("Gemini:")), event.text);
} else if (event.type == "turnComplete") {
break;
}
}
outputAudio(Buffer.concat(audioBuffers));
}
return 0;
}
main().catch(console.error);
+7
View File
@@ -0,0 +1,7 @@
import { createConsola } from "consola";
export const logger = createConsola({
formatOptions: {
date: false,
},
});
+44
View File
@@ -0,0 +1,44 @@
import { Readable } from "stream";
import Speaker from "speaker";
import * as readline from "readline/promises";
import { renderFilled } from "oh-my-logo";
export async function renderLogo(): Promise<void> {
const logo = await renderFilled("LIVE CHAT", {
palette: ["#F8E9D8", "#FFA6EA", "#45DFF8", "#BB8DEB"],
});
console.log(logo);
}
/**
* Plays raw PCM audio (16-bit LE, mono, 24000Hz) through the system speaker.
* @param pcmBuffer - A Buffer of raw PCM data (Int16LE, mono, 24000 Hz).
*/
export function outputAudio(pcmBuffer: Buffer): void {
const speaker = new Speaker({
channels: 1,
bitDepth: 16,
sampleRate: 24000,
});
const readable = new Readable();
readable.push(pcmBuffer);
readable.push(null); // Signal end of stream
readable.pipe(speaker);
}
export function decodePCMData(dataLine: string): Buffer {
// Decode base64 string into a Buffer
return Buffer.from(dataLine, "base64");
}
export async function consoleInput(): Promise<string> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const answer = await rl.question("Your message to Gemini: ");
rl.close();
return answer;
}
+46
View File
@@ -0,0 +1,46 @@
{
"compilerOptions": {
/* Basic Options */
"target": "ES2022",
"module": "CommonJS",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
/* Module Resolution */
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"isolatedModules": true,
/* Type Checking */
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
/* Emit */
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"removeComments": false,
"importHelpers": true,
/* Advanced */
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"],
"ts-node": {
"esm": false,
"experimentalSpecifierResolution": "node"
}
}