This commit is contained in:
geoffsee
2025-06-25 13:20:59 -04:00
committed by Geoff Seemueller
parent 21d6c8604e
commit 554096abb2
86 changed files with 556 additions and 508 deletions

34
packages/coordinators/.gitignore vendored Normal file
View File

@@ -0,0 +1,34 @@
# dependencies (bun install)
node_modules
# output
out
dist
*.tgz
# code coverage
coverage
*.lcov
# logs
logs
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# caches
.eslintcache
.cache
*.tsbuildinfo
# IntelliJ based IDEs
.idea
# Finder (MacOS) folder config
.DS_Store

View File

@@ -0,0 +1,3 @@
# durable_objects
This package exports implementations of durable objects

View File

@@ -0,0 +1,3 @@
import ServerCoordinator from './src/ServerCoordinator';
export { ServerCoordinator };

View File

@@ -0,0 +1,13 @@
{
"name": "@open-gsio/coordinators",
"module": "index.ts",
"type": "module",
"private": true,
"devDependencies": {
"@types/bun": "^1",
"@cloudflare/workers-types": "^4"
},
"peerDependencies": {
"typescript": "^5"
}
}

View File

@@ -0,0 +1,79 @@
import { ProviderRepository } from '@open-gsio/ai/providers/_ProviderRepository.ts';
// @ts-expect-error - don't care
// eslint-disable-next-line import/no-unresolved
import { DurableObject } from 'cloudflare:workers';
export default class ServerCoordinator extends DurableObject {
env;
state;
constructor(state, env) {
super(state, env);
this.state = state;
this.env = env;
}
// Public method to calculate dynamic max tokens
async dynamicMaxTokens(model, input, maxOuputTokens) {
const modelMeta = ProviderRepository.getModelMeta(model, this.env);
// The tokenlimit information is stored in three different keys:
// max_completion_tokens
// context_window
// context_length
if ('max_completion_tokens' in modelMeta) {
return modelMeta.max_completion_tokens;
} else if ('context_window' in modelMeta) {
return modelMeta.context_window;
} else if ('context_length' in modelMeta) {
return modelMeta.context_length;
} else {
return 2000;
}
}
// Public method to retrieve conversation history
async getConversationHistory(conversationId) {
const history = await this.env.KV_STORAGE.get(`conversations:${conversationId}`);
return JSON.parse(history) || [];
}
// Public method to save a message to the conversation history
async saveConversationHistory(conversationId, message) {
const history = await this.getConversationHistory(conversationId);
history.push(message);
await this.env.KV_STORAGE.put(`conversations:${conversationId}`, JSON.stringify(history));
}
async saveStreamData(streamId, data, ttl = 10) {
const expirationTimestamp = Date.now() + ttl * 1000;
// await this.state.storage.put(streamId, { data, expirationTimestamp });
await this.env.KV_STORAGE.put(
`streams:${streamId}`,
JSON.stringify({ data, expirationTimestamp }),
);
}
// New method to get stream data
async getStreamData(streamId) {
const streamEntry = await this.env.KV_STORAGE.get(`streams:${streamId}`);
if (!streamEntry) {
return null;
}
const { data, expirationTimestamp } = JSON.parse(streamEntry);
if (Date.now() > expirationTimestamp) {
// await this.state.storage.delete(streamId); // Clean up expired entry
await this.deleteStreamData(`streams:${streamId}`);
return null;
}
return data;
}
// New method to delete stream data (cleanup)
async deleteStreamData(streamId) {
await this.env.KV_STORAGE.delete(`streams:${streamId}`);
}
}

View File

@@ -0,0 +1,73 @@
import { BunSqliteKVNamespace } from '@open-gsio/server/src/storage/BunSqliteKVNamespace';
class BunDurableObject {
state;
env;
constructor(state: any, env: any) {
this.state = state;
this.env = env;
}
public static idFromName(name: string) {
return name.split('~')[1];
}
public static get(objectId: ObjectId) {
// @ts-expect-error - This shouldn't work but it does.
// the way that env gets assigned
const env = getEnvForObjectId(objectId, this.env);
const state = {};
return new SiteCoordinator(state, env);
}
}
export type ObjectId = string;
function getEnvForObjectId(objectId: ObjectId, env: any): any {
return {
...env,
KV_STORAGE: new BunSqliteKVNamespace(),
};
}
export default class SiteCoordinator extends BunDurableObject {
state;
env;
constructor(state: any, env: any) {
super(state, env);
this.state = state;
this.env = env;
}
async dynamicMaxTokens(input: any, maxOuputTokens: any) {
return 2000;
}
async saveStreamData(streamId: string, data: any, ttl = 10) {
const expirationTimestamp = Date.now() + ttl * 1000;
await this.env.KV_STORAGE.put(
`streams:${streamId}`,
JSON.stringify({ data, expirationTimestamp }),
);
}
async getStreamData(streamId: string) {
const streamEntry = await this.env.KV_STORAGE.get(`streams:${streamId}`);
if (!streamEntry) {
return null;
}
const { data, expirationTimestamp } = JSON.parse(streamEntry);
if (Date.now() > expirationTimestamp) {
await this.deleteStreamData(`streams:${streamId}`);
return null;
}
return data;
}
async deleteStreamData(streamId: string) {
await this.env.KV_STORAGE.delete(`streams:${streamId}`);
}
}

View File

@@ -0,0 +1,28 @@
{
"compilerOptions": {
// Environment setup & latest features
"lib": ["ESNext"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}