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

Refacto complète du package #7

Merged
merged 3 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
node_modules
yarn.lock
es
.DS_Store
.DS_Store
.turbo
yarn-error.log
.env
84 changes: 42 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,68 +6,68 @@ This repository is a tentative of porting the [genai-impact/ecologits python lib

## Installation

### Using npm
Installation depends on the model providers that you use in your code.

### Open AI

```bash
npm install @genai-impact/ecologits.js
npm install @genai-impact/ecologits-openai
```

### Using yarn
### Mistral AI

```bash
yarn add @genai-impact/ecologits.js
npm install @genai-impact/ecologits-mistral
```

## Usage

**Warning**: as usual, you'll need to provide your credentials to your API provider in the environment variables as instructed by them, or pass them directly to the client as you would normally.
The usage will depend on the model provider used in your code.
But the principle is simple :

- import the provider wrapper from `@genai-impact/ecologits-<provider>`
- use it (e.g. `MistralClient` / `OpenAI`) as you would usually.
- The wrapper adds an `impacts` attribute to the response containing EcoLogits metrics.

> [!WARNING] As usual, you'll need to provide your credentials to your API provider in the environment variables as instructed by them, or pass them directly to the client as you would normally.

```ts
import { Ecologits, type Impacts } from "@genai-impact/ecologits.js";
import OpenAI from "openai";
import MistralClient from "@genai-impact/ecologits-mistral";
import { ChatCompletionResponse } from "@mistralai/mistralai";
import { Impacts } from "core";

const apiKey = process.env.MISTRAL_API_KEY;

Ecologits.init(); // Call ecologits **before** any other relevant AI package import
const client = new MistralClient(apiKey);

const client = new OpenAI();
const main = async () => {
const response = (await client.chat.completions.create({
messages: [{ role: "user", content: "Tell me a funny joke!" }],
model: "gpt-3.5-turbo",
})) as OpenAI.Chat.Completions.ChatCompletion & { impacts: Impacts };

console.log(
`Joke: ${response.choices[0].message.content}`
);
console.log(
`Token generated: ${response.usage.completion_tokens} tokens`
);

// Get estimated environmental impacts of the inference
console.log(
`Energy consumption: ${response.impacts.energy.value} ${response.impacts.energy.unit}`
);
console.log(
`GHG emissions: ${response.impacts.gwp.value} ${response.impacts.gwp.unit}`
);
try {
const response = (await client.chat({
model: "mistral-tiny",
messages: [{ role: "user", content: "What is the best French cheese?" }],
})) as ChatCompletionResponse & { impacts: Impacts };
// Get estimated environmental impacts of the inference
console.log(
`Energy consumption: ${response.impacts.energy.value} ${response.impacts.energy.unit}`
);
console.log(
`GHG emissions: ${response.impacts.gwp.value} ${response.impacts.gwp.unit}`
);
} catch (e) {
console.error(e);
throw e;
}
};
main();
```

## Porting Status
## Contributing

- [x] `openAI` tracer
- [ ] `mistral` tracer (branch `feat/mistral_tracer`)
- [ ] `anthropic` tracer
- [ ] `huggingface` tracer
- [ ] `cohere` tracer
Look for open issues and feel free to contribute to this project by opening an issue or a pull request.
We're always open to covering more model providers.

## Current challenges
### Current challenges

- [ ] 🔥 Patching the providers responses with the `impacts` object like in the python library (with seamless types exposition)
- [ ] publishing the package to npm
- [ ] Setting up CI with automatic publication to npm.js of each package
- [ ] port tests from python to js
- [ ] reduce work to keep the library up-to-date with the python library (csv files, etc)

## Contributing

Feel free to contribute to this project by opening an issue or a pull request.
- [ ] reduce work to keep the library up-to-date with the python library (etc)
15 changes: 15 additions & 0 deletions libs/core/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "core",
"version": "0.0.1",
"description": "Core library for the Ecologits project, port of the ecologits methodology to TypeScript",
"type": "module",
"main": "es/index.js",
"types": "es/index.d.ts",
"source": "src/index.ts",
"scripts": {
"build": "tsc",
"prepublishOnly": "yarn build",
"watch": "tsc-watch --noClear",
"clean": "rm -rf es"
}
}
59 changes: 26 additions & 33 deletions src/tracers/utils.ts → libs/core/src/ecologits.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fetch from "node-fetch";
import computeLlmImpacts from "../impacts/dag";
import { DEFAULT_IMPACT } from "../impacts/default";
import computeLlmImpacts from "./impacts/dag.js";
import { DEFAULT_IMPACT } from "./impacts/default.js";

type ModelData = {
provider: string;
Expand All @@ -21,36 +21,6 @@ class EcoLogitsData {
this.data = data;
}

static async build() {
const url =
"https://raw.githubusercontent.com/genai-impact/ecologits/main/ecologits/data/models.csv";
return fetch(url).then((res) => {
return res.text().then(
(text) =>
new EcoLogitsData(
text
.split("\n")
.slice(1, text.length)
.map((line) => {
const infos = line.split(",");
return {
provider: infos[0],
name: infos[1],
totalParameters: infos[2]
.split(";")
.map((x) => parseFloat(x)),
activeParameters: infos[3]
.split(";")
.map((x) => parseFloat(x)),
warnings: infos[4],
sources: infos[5],
} as ModelData;
})
)
);
});
}

findModel(provider: string, name: string): ModelData | undefined {
return this.data.find(
(model) => model.provider === provider && model.name === name
Expand Down Expand Up @@ -81,5 +51,28 @@ class EcoLogitsData {
);
}
}
const url =
"https://raw.githubusercontent.com/genai-impact/ecologits/main/ecologits/data/models.csv";
const ecoLogitsData: EcoLogitsData = await fetch(url).then((res) => {
return res.text().then(
(text) =>
new EcoLogitsData(
text
.split("\n")
.slice(1, text.length)
.map((line) => {
const infos = line.split(",");
return {
provider: infos[0],
name: infos[1],
totalParameters: infos[2].split(";").map((x) => parseFloat(x)),
activeParameters: infos[3].split(";").map((x) => parseFloat(x)),
warnings: infos[4],
sources: infos[5],
} as ModelData;
})
)
);
});

export default EcoLogitsData;
export default ecoLogitsData;
39 changes: 28 additions & 11 deletions src/impacts/dag.ts → libs/core/src/impacts/dag.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Llm from "./llm";
import { ImpactMetric, Impacts } from "../types.js";
import * as Llm from "./llm.js";

const MODEL_QUANTIZATION_BITS = 4;

Expand Down Expand Up @@ -85,14 +86,30 @@ const DEFAULT_CALCULATED_PROPS: CalculatedProps = {
requestEmbodiedAdpe: 0,
requestEmbodiedPe: 0,
};
const orderedFuncs = [
"gpuEnergy",
"generationLatency",
"modelRequiredMemory",
"gpuRequiredCount",
"serverEnergy",
"requestEnergy",
"requestUsageGwp",
"requestUsageAdpe",
"requestUsagePe",
"serverGpuEmbodiedGwp",
"serverGpuEmbodiedAdpe",
"serverGpuEmbodiedPe",
"requestEmbodiedGwp",
"requestEmbodiedAdpe",
"requestEmbodiedPe",
];

function dagExecute(props: ComputeLLmImpactsProps) {
const allProps: ComputeLLmImpactsProps & CalculatedProps = {
...DEFAULT_CALCULATED_PROPS, // add default values to const that are going to be calculated
...props,
};
const funcs = Object.keys(Llm); // get all calcul functions
return funcs.reduce((acc, fn) => {
return orderedFuncs.reduce((acc, fn) => {
const res = Llm[fn as keyof typeof Llm](allProps);
allProps[fn as keyof typeof DEFAULT_CALCULATED_PROPS] = res;
return { ...acc, [fn]: res };
Expand Down Expand Up @@ -123,7 +140,7 @@ export default function computeLlmImpacts(
ifElectricityMixGwp: number = IF_ELECTRICITY_MIX_GWP,
ifElectricityMixAdpe: number = IF_ELECTRICITY_MIX_ADPE,
ifElectricityMixPe: number = IF_ELECTRICITY_MIX_PE
) {
): Impacts {
const results = dagExecute({
modelActiveParameterCount,
modelTotalParameterCount,
Expand All @@ -149,43 +166,43 @@ export default function computeLlmImpacts(
ifElectricityMixAdpe,
ifElectricityMixPe,
});
const energy = {
const energy: ImpactMetric = {
type: "energy",
name: "Energy",
unit: "kWh",
value: results["requestEnergy"],
};
const gwpUsage = {
const gwpUsage: ImpactMetric = {
type: "GWP",
name: "Global Warming Potential",
unit: "kgCO2eq",
value: results["requestUsageGwp"],
};
const adpeUsage = {
const adpeUsage: ImpactMetric = {
type: "ADPe",
name: "Abiotic Depletion Potential (elements)",
unit: "kgSbeq",
value: results["requestUsageAdpe"],
};
const peUsage = {
const peUsage: ImpactMetric = {
type: "PE",
name: "Primary Energy",
unit: "MJ",
value: results["requestUsagePe"],
};
const gwpEmbodied = {
const gwpEmbodied: ImpactMetric = {
type: "GWP",
name: "Global Warming Potential",
unit: "kgCO2eq",
value: results["requestEmbodiedGwp"],
};
const adpeEmbodied = {
const adpeEmbodied: ImpactMetric = {
type: "ADPe",
name: "Abiotic Depletion Potential (elements)",
unit: "kgSbeq",
value: results["requestEmbodiedAdpe"],
};
const peEmbodied = {
const peEmbodied: ImpactMetric = {
type: "PE",
name: "Primary Energy",
unit: "MJ",
Expand Down
12 changes: 7 additions & 5 deletions src/impacts/default.ts → libs/core/src/impacts/default.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
const energy = {
import { Impacts, ImpactMetric } from "../types.js";

const energy: ImpactMetric = {
type: "energy",
name: "Energy",
unit: "kWh",
value: 0,
};
const gwp = {
const gwp: ImpactMetric = {
type: "GWP",
name: "Global Warming Potential",
unit: "kgCO2eq",
value: 0,
};
const adpe = {
const adpe: ImpactMetric = {
type: "ADPe",
name: "Abiotic Depletion Potential (elements)",
unit: "kgSbeq",
value: 0,
};
const pe = {
const pe: ImpactMetric = {
type: "PE",
name: "Primary Energy",
unit: "MJ",
value: 0,
};

export const DEFAULT_IMPACT = {
export const DEFAULT_IMPACT: Impacts = {
energy,
gwp: gwp,
adpe: adpe,
Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions libs/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as computeLlmImpacts } from "./impacts/dag.js";
export { default, default as ecoLogitsData } from "./ecologits.js";
export type * from "./types.js";
24 changes: 24 additions & 0 deletions libs/core/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export type ImpactMetric = {
type: "energy" | "GWP" | "ADPe" | "PE";
name: string;
unit: string;
value: number;
};

export type Impacts = {
energy: ImpactMetric;
gwp: ImpactMetric;
adpe: ImpactMetric;
pe: ImpactMetric;
usage: {
energy: ImpactMetric;
gwp: ImpactMetric;
adpe: ImpactMetric;
pe: ImpactMetric;
};
embodied: {
gwp: ImpactMetric;
adpe: ImpactMetric;
pe: ImpactMetric;
};
};
19 changes: 19 additions & 0 deletions libs/core/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"includes": ["src/@types/*.d.ts, @types/*.d.ts"],
"compilerOptions": {
"rootDir": "./src",
"outDir": "./es",
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"resolveJsonModule": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"jsx": "react-jsx"
}
}
Loading