Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: reorganize code structure #48

Merged
merged 10 commits into from
Aug 1, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
refactor: reorganize code structure
Dam-Buty committed Jul 30, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit de963f8cfa65e2e7c43f16acea9d861407aa4130
2,933 changes: 2,933 additions & 0 deletions docs/README.md

Large diffs are not rendered by default.

942 changes: 0 additions & 942 deletions docs/api.md

This file was deleted.

48 changes: 31 additions & 17 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,14 @@ import { ReadStream } from 'fs';
import { v4 as uuidv4 } from 'uuid';

import { LiteralClient } from '.';
import {
Dataset,
DatasetExperiment,
DatasetExperimentItem,
DatasetItem,
DatasetType
} from './evaluation/dataset';
import { Score, ScoreConstructor } from './evaluation/score';
import {
GenerationsFilter,
GenerationsOrderBy,
@@ -16,32 +24,23 @@ import {
ThreadsFilter,
ThreadsOrderBy
} from './filter';
import { Attachment } from './observability/attachment';
import {
Generation,
IGenerationMessage,
PersistedGeneration
} from './generation';
} from './observability/generation';
import { Step, StepType } from './observability/step';
import { CleanThreadFields, Thread } from './observability/thread';
import { Prompt } from './prompt-engineering/prompt';
import {
Attachment,
CleanThreadFields,
Dataset,
DatasetExperiment,
DatasetExperimentItem,
DatasetItem,
DatasetType,
Environment,
Maybe,
OmitUtils,
PaginatedResponse,
Prompt,
Score,
ScoreConstructor,
Step,
StepType,
Thread,
User,
Utils
} from './types';
} from './utils';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require('../package.json');
@@ -892,6 +891,7 @@ export class API {
metadata?: Maybe<Record<string, any>>;
participantId?: Maybe<string>;
tags?: Maybe<string[]>;
environment?: Maybe<Environment>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We add environment from Willy's developments last week?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, turns out it wasn't taken into account in this case

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm not sure we need this. The environment is passed as a HTTP header at the sdk level. it should not show in function signatures.

Furthermore, I don't think it is useful on attachments since the attachments are linked to steps and steps have an environment.

}): Promise<CleanThreadFields>;

/**
@@ -910,15 +910,17 @@ export class API {
name?: Maybe<string>,
metadata?: Maybe<Record<string, any>>,
participantId?: Maybe<string>,
tags?: Maybe<string[]>
tags?: Maybe<string[]>,
environment?: Maybe<Environment>
): Promise<CleanThreadFields>;

async upsertThread(
threadIdOrOptions: any,
name?: Maybe<string>,
metadata?: Maybe<Record<string, any>>,
participantId?: Maybe<string>,
tags?: Maybe<string[]>
tags?: Maybe<string[]>,
environment?: Maybe<Environment>
): Promise<CleanThreadFields> {
let threadId = threadIdOrOptions;
if (typeof threadIdOrOptions === 'object') {
@@ -927,6 +929,13 @@ export class API {
metadata = threadIdOrOptions.metadata;
participantId = threadIdOrOptions.participantId;
tags = threadIdOrOptions.tags;
environment = threadIdOrOptions.environment;
}

const originalEnvironment = this.environment;

if (environment && environment !== originalEnvironment) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we adding this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that the environment was part of ThreadConstructor, like for example i can do client.thread({ environment: 'prod' }).wrap but then it is completely ignored by the code.

This code in upsertThread just updates the api.environment for the time it takes to upsert the thread, then reverts the change.

I'm not sure what you mean about attachments though ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is legacy, from now on environment is handled at the header level. We are just keeping the arg for backward compatiblity

this.environment = environment;
}

const query = `
@@ -958,6 +967,11 @@ export class API {
};

const response = await this.makeGqlCall(query, variables);

if (environment && environment !== originalEnvironment) {
this.environment = originalEnvironment;
}

return new Thread(this.client, response.data.upsertThread);
}

237 changes: 237 additions & 0 deletions src/evaluation/dataset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
import { API } from '../api';
import { Maybe, OmitUtils, Utils } from '../utils';
import { ScoreConstructor } from './score';

export type DatasetType = 'key_value' | 'generation';

class DatasetFields extends Utils {
id!: string;
createdAt!: string;
name?: Maybe<string>;
description?: Maybe<string>;
metadata!: Record<string, any>;
items!: Array<OmitUtils<DatasetItem>>;
type?: DatasetType;
}

export type DatasetConstructor = OmitUtils<DatasetFields>;

export class Dataset extends DatasetFields {
api: API;

/**
* Constructs a new Dataset instance.
* @param api - The API instance to interact with backend services.
* @param data - The initial data for the dataset.
*/
constructor(api: API, data: DatasetConstructor) {
super();
this.api = api;
Object.assign(this, data);
if (!this.items) {
this.items = [];
}
if (!this.type) {
this.type = 'key_value';
}
}

/**
* Updates the dataset with new data.
* @param dataset - The dataset data to update.
* @returns The updated dataset instance.
*/
async update(dataset: {
name?: Maybe<string>;
description?: Maybe<string>;
metadata?: Maybe<Record<string, any>>;
}) {
const update_res = await this.api.updateDataset(this.id, dataset);
this.name = update_res.name;
this.description = update_res.description;
this.metadata = update_res.metadata;
}

/**
* Deletes the dataset.
* @returns A promise that resolves when the dataset is deleted.
*/
async delete() {
return this.api.deleteDataset(this.id);
}

/**
* Creates a new item in the dataset.
* @param datasetItem - The new item to be added to the dataset.
* @returns The newly created dataset item.
*/
async createItem(datasetItem: {
input: Record<string, any>;
expectedOutput?: Maybe<Record<string, any>>;
metadata?: Maybe<Record<string, any>>;
}) {
const item = await this.api.createDatasetItem(this.id, datasetItem);

this.items.push(item);
return item;
}

/**
* Deletes an item from the dataset.
* @param id - The ID of the item to delete.
* @returns The deleted dataset item.
*/
async deleteItem(id: string) {
const deletedItem = await this.api.deleteDatasetItem(id);
if (this.items) {
this.items = this.items.filter((item) => item.id !== id);
}
return deletedItem;
}

/**
* Creates a new experiment associated with the dataset.
* @param experiment - The experiment details including name, optional prompt ID, and parameters.
* @returns A new instance of DatasetExperiment containing the created experiment.
*/
async createExperiment(experiment: {
name: string;
promptId?: string;
params?: Record<string, any> | Array<Record<string, any>>;
}) {
const datasetExperiment = await this.api.createExperiment({
name: experiment.name,
datasetId: this.id,
promptId: experiment.promptId,
params: experiment.params
});
return new DatasetExperiment(this.api, datasetExperiment);
}

/**
* Adds a step to the dataset.
* @param stepId - The ID of the step to add.
* @param metadata - Optional metadata for the step.
* @returns The added dataset item.
* @throws Error if the dataset type is 'generation'.
*/
public async addStep(
stepId: string,
metadata?: Maybe<Record<string, unknown>>
) {
if (this.type === 'generation') {
throw new Error('Cannot add steps to a generation dataset');
}
const item = await this.api.addStepToDataset(this.id, stepId, metadata);
this.items.push(item);
return item;
}

/**
* Adds a generation to the dataset.
* @param generationId - The ID of the generation to add.
* @param metadata - Optional metadata for the generation.
* @returns The added dataset item.
*/
public async addGeneration(
generationId: string,
metadata?: Maybe<Record<string, unknown>>
) {
const item = await this.api.addGenerationToDataset(
this.id,
generationId,
metadata
);
this.items.push(item);
return item;
}

public async addGenerations(generationIds?: string[]) {
if (generationIds == undefined || generationIds?.length === 0) {
return [];
}

const items = await this.api.addGenerationsToDataset(
this.id,
generationIds
);
this.items = this.items.concat(items);
return items;
}
}

export class DatasetItem extends Utils {
id!: string;
createdAt!: string;
datasetId!: string;
metadata!: Record<string, any>;
input!: Record<string, any>;
expectedOutput?: Maybe<Record<string, any>>;
intermediarySteps!: Array<Record<string, any>>;

constructor(data: OmitUtils<DatasetItem>) {
super();
Object.assign(this, data);
}
}

class DatasetExperimentItemFields extends Utils {
id?: string;
datasetExperimentId!: string;
datasetItemId?: string;
experimentRunId?: string;
scores!: ScoreConstructor[];
input?: Record<string, any>;
output?: Record<string, any>;
}

export class DatasetExperiment extends Utils {
id!: string;
createdAt!: string;
name!: string;
datasetId?: string;
promptId?: string;
api: API;
params!: Record<string, any> | Array<Record<string, any>>;
items!: DatasetExperimentItem[];

constructor(api: API, data: OmitUtils<DatasetExperiment>) {
super();
this.api = api;
Object.assign(this, data);
if (!this.items) {
this.items = [];
}
}

async log(
itemFields: Omit<
OmitUtils<DatasetExperimentItemFields>,
'id' | 'datasetExperimentId'
>
) {
const currentStore = this.api.client.store.getStore();
const experimentRunId = currentStore?.currentExperimentRunId;

const datasetExperimentItem = new DatasetExperimentItem({
...itemFields,
datasetExperimentId: this.id,
...(experimentRunId && { experimentRunId })
});

const item = await this.api.createExperimentItem(datasetExperimentItem);

this.items.push(item);
return item;
}
}

export type DatasetExperimentItemConstructor =
OmitUtils<DatasetExperimentItemFields>;

export class DatasetExperimentItem extends DatasetExperimentItemFields {
constructor(data: DatasetExperimentItemConstructor) {
super();
Object.assign(this, data);
}
}
Loading