carnot-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +103 -0
- package/dist/core/CarnotAgent.d.ts +28 -0
- package/dist/core/CarnotAgent.d.ts.map +1 -0
- package/dist/core/CarnotEngine.d.ts +10 -0
- package/dist/core/CarnotEngine.d.ts.map +1 -0
- package/dist/core/ContextualRazor.d.ts +9 -0
- package/dist/core/ContextualRazor.d.ts.map +1 -0
- package/dist/core/ThermodynamicLedger.d.ts +19 -0
- package/dist/core/ThermodynamicLedger.d.ts.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +32 -0
- package/src/core/CarnotAgent.ts +79 -0
- package/src/core/CarnotEngine.ts +71 -0
- package/src/core/ContextualRazor.ts +26 -0
- package/src/core/ThermodynamicLedger.ts +34 -0
- package/src/index.ts +4 -0
- package/src/types.ts +17 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FleuryTheGodDev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>carnot-sdk</h1>
|
|
3
|
+
<p>Zero-latency edge routing for LLMs using Shannon entropy.</p>
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/carnot-sdk)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[]()
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
### The Problem
|
|
11
|
+
Sending every user prompt to massive cloud models is a thermodynamic waste. The industry treats all prompts as equal, burning compute and dollars on simple queries that could be handled locally.
|
|
12
|
+
|
|
13
|
+
### The Solution
|
|
14
|
+
**Carnot** is an 11kb stateless routing protocol that evaluates the "cognitive weight" of a prompt before a single token is sent over the network.
|
|
15
|
+
|
|
16
|
+
It runs entirely on the edge (client-side) and decides in **< 5ms** whether to route to:
|
|
17
|
+
* **Local Execution** (Regex/Cache)
|
|
18
|
+
* **Edge Inference** (On-device small models)
|
|
19
|
+
* **Cloud Relocation** (Frontier massive-parameter models)
|
|
20
|
+
|
|
21
|
+
### How it Works (Under the Hood)
|
|
22
|
+
We don't use AI to route AI. We use deterministic mathematics. Carnot relies on three O(N) heuristics:
|
|
23
|
+
1. **Shannon Entropy Engine:** Calculates the character unpredictability of the prompt.
|
|
24
|
+
2. **Contextual Razor:** Uses DJB2 hashing to detect and eliminate redundant system prompts in conversational loops.
|
|
25
|
+
3. **Academic/Jargon Detection:** Identifies high lexical density to prevent routing complex philosophy to small models.
|
|
26
|
+
|
|
27
|
+
### Installation
|
|
28
|
+
|
|
29
|
+
npm install carnot-sdk
|
|
30
|
+
|
|
31
|
+
### Usage
|
|
32
|
+
|
|
33
|
+
typescript
|
|
34
|
+
import { CarnotAgent, ComputeTier } from 'carnot-sdk';
|
|
35
|
+
|
|
36
|
+
const agent = new CarnotAgent();
|
|
37
|
+
|
|
38
|
+
async function handleInference(rawPrompt: string) {
|
|
39
|
+
// Analyze the prompt in < 5ms
|
|
40
|
+
const verdict = agent.execute(rawPrompt);
|
|
41
|
+
|
|
42
|
+
if (verdict.tier === ComputeTier.CLOUD_GOD_TIER) {
|
|
43
|
+
return await expensiveCloudLLM.fetch(rawPrompt);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Route to local or edge...
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Run the Benchmark
|
|
51
|
+
Want to see the routing logic in action? Clone the repo and run:
|
|
52
|
+
|
|
53
|
+
node examples/enterprise-demo.js
|
|
54
|
+
|
|
55
|
+
### Why Open Source?
|
|
56
|
+
The compute crisis in AI is an infrastructure problem that requires a community standard. By open-sourcing the routing engine, we ensure it becomes the universal optimization layer for any developer building with LLMs, regardless of their cloud provider.
|
|
57
|
+
|
|
58
|
+
### Concrete Integration Scenarios
|
|
59
|
+
|
|
60
|
+
Carnot is a routing engine. It does not execute the AI, it tells your app *where* to execute it. Here is how to implement the 3 tiers in a real-world application:
|
|
61
|
+
|
|
62
|
+
#### 1. Local Execution (Regex / Cache / App State)
|
|
63
|
+
**When to use:** Simple factual queries, greetings, or UI interactions.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// Example: User asks for app settings
|
|
67
|
+
if (verdict.tier === ComputeTier.LOCAL_LOGIC) {
|
|
68
|
+
return getAppSettingsFromLocalDatabase(); // Takes 0.001ms. $0 cost.
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### 2. Edge Inference (On-Device Small Models)
|
|
73
|
+
**When to use:** Summarizations, basic translations, or formatting. Tasks too complex for Regex, but not requiring frontier logic.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// Example: User wants a text summarized
|
|
77
|
+
if (verdict.tier === ComputeTier.EDGE_LLM) {
|
|
78
|
+
// Route to an on-device model like Llama-3-8B (using react-native-llama, for instance)
|
|
79
|
+
const result = await LocalDeviceModel.generate(rawPrompt); // Takes ~2s. $0 cost.
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### 3. Cloud Relocation (Frontier Massive-Parameter Models)
|
|
85
|
+
**When to use:** Highly complex reasoning, deep philosophical analysis, or code generation that small models cannot handle.
|
|
86
|
+
```typescript
|
|
87
|
+
// Example: Complex philosophical prompt
|
|
88
|
+
if (verdict.tier === ComputeTier.CLOUD_GOD_TIER) {
|
|
89
|
+
// Safely send to OpenAI/Anthropic, knowing you avoided paying for the simple queries
|
|
90
|
+
const response = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
91
|
+
body: JSON.stringify({ model: "gpt-4o", messages: [{ role: "user", content: rawPrompt }] })
|
|
92
|
+
});
|
|
93
|
+
return response;
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**The Result:** Your users get the exact same experience, but your infrastructure costs drop drastically because simple tasks never touch the network.
|
|
98
|
+
|
|
99
|
+
### License
|
|
100
|
+
MIT
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ComputeTier, ThermodynamicRecord, ObserverCallback } from '../types';
|
|
2
|
+
export interface CarnotOptions {
|
|
3
|
+
forceTier?: ComputeTier;
|
|
4
|
+
}
|
|
5
|
+
export declare class CarnotAgent {
|
|
6
|
+
private readonly razor;
|
|
7
|
+
private readonly engine;
|
|
8
|
+
private readonly ledger;
|
|
9
|
+
private observers;
|
|
10
|
+
constructor();
|
|
11
|
+
/**
|
|
12
|
+
* Evaluates the cognitive weight of a prompt to route it to the optimal compute tier.
|
|
13
|
+
* @param rawPrompt The raw string input from the user.
|
|
14
|
+
* @param options Optional overrides. Use `forceTier` to bypass heuristics and maintain conversation state with a cloud model.
|
|
15
|
+
* @returns A ThermodynamicRecord containing the routing decision and estimated savings.
|
|
16
|
+
*/
|
|
17
|
+
execute(rawPrompt: string, options?: CarnotOptions): ThermodynamicRecord;
|
|
18
|
+
private buildForcedDecision;
|
|
19
|
+
onRecordGenerated(callback: ObserverCallback): () => void;
|
|
20
|
+
getImpactMetrics(): {
|
|
21
|
+
totalDollarsSaved: number;
|
|
22
|
+
totalWattsSaved: number;
|
|
23
|
+
requestsProcessed: number;
|
|
24
|
+
};
|
|
25
|
+
flushRecords(): ThermodynamicRecord[];
|
|
26
|
+
private notifyObservers;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=CarnotAgent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CarnotAgent.d.ts","sourceRoot":"","sources":["../../src/core/CarnotAgent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAmB,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAM/F,MAAM,WAAW,aAAa;IAC1B,SAAS,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,SAAS,CAAoC;;IASrD;;;;;OAKG;IACI,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,mBAAmB;IAqB/E,OAAO,CAAC,mBAAmB;IASpB,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,IAAI;IAKzD,gBAAgB;;;;;IAIhB,YAAY,IAAI,mBAAmB,EAAE;IAI5C,OAAO,CAAC,eAAe;CAM1B"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RoutingDecision } from '../types';
|
|
2
|
+
export declare class CarnotEngine {
|
|
3
|
+
private static readonly THRESHOLD_EDGE;
|
|
4
|
+
private static readonly THRESHOLD_CLOUD;
|
|
5
|
+
route(prompt: string): RoutingDecision;
|
|
6
|
+
private calculateShannonEntropy;
|
|
7
|
+
private calculateSyntacticDepth;
|
|
8
|
+
private buildDecision;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=CarnotEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CarnotEngine.d.ts","sourceRoot":"","sources":["../../src/core/CarnotEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,eAAe,EAAE,MAAM,UAAU,CAAC;AAExD,qBAAa,YAAY;IACrB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAM;IAC5C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAM;IAEtC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe;IA4C7C,OAAO,CAAC,uBAAuB;IAQ/B,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,aAAa;CAGxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContextualRazor.d.ts","sourceRoot":"","sources":["../../src/core/ContextualRazor.ts"],"names":[],"mappings":"AAAA,qBAAa,eAAe;IACxB,OAAO,CAAC,eAAe,CAAuB;IACvC,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE;IAkBxF,OAAO,CAAC,UAAU;CAKrB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RoutingDecision, ThermodynamicRecord } from '../types';
|
|
2
|
+
export declare class ThermodynamicLedger {
|
|
3
|
+
private static readonly CLOUD_COST_PER_TOKEN_USD;
|
|
4
|
+
private static readonly CLOUD_COST_PER_TOKEN_WATT;
|
|
5
|
+
private static readonly EDGE_COST_MULTIPLIER;
|
|
6
|
+
private static readonly LOCAL_COST_MULTIPLIER;
|
|
7
|
+
private ledger;
|
|
8
|
+
private readonly MAX_BUFFER_SIZE;
|
|
9
|
+
recordDecision(prompt: string, decision: RoutingDecision): ThermodynamicRecord;
|
|
10
|
+
getAggregateValue(): {
|
|
11
|
+
totalDollarsSaved: number;
|
|
12
|
+
totalWattsSaved: number;
|
|
13
|
+
requestsProcessed: number;
|
|
14
|
+
};
|
|
15
|
+
exportRecords(): ThermodynamicRecord[];
|
|
16
|
+
private estimateTokens;
|
|
17
|
+
private calculateCost;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=ThermodynamicLedger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThermodynamicLedger.d.ts","sourceRoot":"","sources":["../../src/core/ThermodynamicLedger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,eAAe,EAAE,mBAAmB,EAAc,MAAM,UAAU,CAAC;AACzF,qBAAa,mBAAmB;IAC5B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAAW;IAC3D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAU;IAC3D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAQ;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IACtD,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IACjC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,mBAAmB;IAiB9E,iBAAiB,IAAI;QAAE,iBAAiB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE;IAGtG,aAAa,IAAI,mBAAmB,EAAE;IAC7C,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,aAAa;CAGxB"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";class t{constructor(){this.lastContextHash=null}shave(t){const e=t.match(/<context>([\s\S]*?)<\/context>|([\s\S]*?)\n\n(?!.*\n\n)/);if(!e)return{optimizedPrompt:t,contextTokensSaved:0};const s=e[1]||e[2]||"",r=t.replace(e[0],"").trim();if(!s||!r)return{optimizedPrompt:t,contextTokensSaved:0};const o=this.simpleHash(s);if(o===this.lastContextHash){const t=`[CTX_CACHED] ${r}`,e=Math.ceil(s.length/4);return this.lastContextHash=o,{optimizedPrompt:t,contextTokensSaved:e}}return this.lastContextHash=o,{optimizedPrompt:t,contextTokensSaved:0}}simpleHash(t){let e=5381;for(let s=0;s<t.length;s++)e=(e<<5)+e+t.charCodeAt(s),e&=e;return e.toString(36)}}var e;exports.ComputeTier=void 0,(e=exports.ComputeTier||(exports.ComputeTier={})).LOCAL_LOGIC="LOCAL_LOGIC",e.EDGE_LLM="EDGE_LLM",e.CLOUD_GOD_TIER="CLOUD_GOD_TIER";class s{route(t){const e=t.trim();if(0===e.length)return this.buildDecision(0,0,0,0,0,exports.ComputeTier.LOCAL_LOGIC,"Empty prompt");if(e.length<25)return this.buildDecision(0,0,0,0,0,exports.ComputeTier.LOCAL_LOGIC,"Sub-25 char. Local.");const r=e.toLowerCase().split(/[\s,.!?;:'"()]+/).filter(t=>t.length>0);if(/[\d]+\s*[+\-*/]\s*[\d]+/.test(e)&&r.length<15)return this.buildDecision(0,0,0,0,0,exports.ComputeTier.LOCAL_LOGIC,"Basic arithmetic. Local.");const o=this.calculateShannonEntropy(e),i=this.calculateSyntacticDepth(e),n=(e.match(/ /g)||[]).length,a=e.length>20?Math.max(0,1-n/(e.length/5)):0;let l=new Set(r).size/Math.max(r.length,1),c=Math.min(l,.5);if(r.filter(t=>t.length>7).length/Math.max(r.length,1)>.3&&a<=.8)return this.buildDecision(100,o,c,i,a,exports.ComputeTier.CLOUD_GOD_TIER,"Academic/Jargon density. Forced Cloud.");let h=.5*o+.3*c+.2*i;a>.8&&(h*=.1);const d=Math.min(100,h/3.5*100);let u,C;return a>.8?(u=exports.ComputeTier.LOCAL_LOGIC,C="Gibberish detected. Blocked."):d<s.THRESHOLD_EDGE?(u=exports.ComputeTier.LOCAL_LOGIC,C="Low density. Local."):d<s.THRESHOLD_CLOUD?(u=exports.ComputeTier.EDGE_LLM,C="Moderate complexity. Edge."):(u=exports.ComputeTier.CLOUD_GOD_TIER,C="High cognitive weight. Cloud."),this.buildDecision(d,o,c,i,a,u,C)}calculateShannonEntropy(t){const e=new Map,s=t.length;for(let r=0;r<s;r++){const s=t[r];e.set(s,(e.get(s)||0)+1)}let r=0;return e.forEach(t=>{const e=t/s;e>0&&(r-=e*Math.log2(e))}),r}calculateSyntacticDepth(t){const e=t.length;if(0===e)return 0;let s=0;for(let r=0;r<e;r++){const e=t[r];","!==e&&";"!==e&&"("!==e&&")"!==e&&":"!==e||s++}return s/Math.max(e,1)*100}buildDecision(t,e,s,r,o,i,n){return{tier:i,entropyScore:Math.round(100*t)/100,metrics:{shannonEntropy:Math.round(1e3*e)/1e3,lexicalDensity:Math.round(1e3*s)/1e3,syntacticDepth:Math.round(1e3*r)/1e3,noiseProbability:Math.round(100*o)/100},reasoning:n}}}s.THRESHOLD_EDGE=55,s.THRESHOLD_CLOUD=85;class r{constructor(){this.ledger=[],this.MAX_BUFFER_SIZE=1e3}recordDecision(t,e){const s=this.estimateTokens(t),o=this.calculateCost(s,1);let i=1;e.tier===exports.ComputeTier.EDGE_LLM&&(i=r.EDGE_COST_MULTIPLIER),e.tier===exports.ComputeTier.LOCAL_LOGIC&&(i=r.LOCAL_COST_MULTIPLIER);const n=this.calculateCost(s,i),a={id:Math.random().toString(36).slice(2,13),timestamp:Date.now(),promptPreview:t.substring(0,50)+(t.length>50?"...":""),routingDecision:e,legacyCost:o,actualCost:n,savings:{dollars:o.dollars-n.dollars,estimatedWatts:o.estimatedWatts-n.estimatedWatts}};return this.ledger.length>=this.MAX_BUFFER_SIZE&&this.ledger.shift(),this.ledger.push(a),a}getAggregateValue(){return this.ledger.reduce((t,e)=>(t.totalDollarsSaved+=e.savings.dollars,t.totalWattsSaved+=e.savings.estimatedWatts,t.requestsProcessed+=1,t),{totalDollarsSaved:0,totalWattsSaved:0,requestsProcessed:0})}exportRecords(){const t=[...this.ledger];return this.ledger=[],t}estimateTokens(t){return Math.ceil(t.length/4)}calculateCost(t,e){return{dollars:parseFloat((t*r.CLOUD_COST_PER_TOKEN_USD*e).toFixed(6)),estimatedWatts:parseFloat((t*r.CLOUD_COST_PER_TOKEN_WATT*e).toFixed(4))}}}r.CLOUD_COST_PER_TOKEN_USD=1e-5,r.CLOUD_COST_PER_TOKEN_WATT=.0015,r.EDGE_COST_MULTIPLIER=.05,r.LOCAL_COST_MULTIPLIER=.001,exports.CarnotAgent=class{constructor(){this.observers=new Set,this.razor=new t,this.engine=new s,this.ledger=new r}execute(t,e){const{optimizedPrompt:s,contextTokensSaved:r}=this.razor.shave(t),o=e?.forceTier?this.buildForcedDecision(e.forceTier,s):this.engine.route(s),i=this.ledger.recordDecision(s,o);return i.contextTokensShaved=r,this.observers.size>0&&Promise.resolve().then(()=>this.notifyObservers(i)),i}buildForcedDecision(t,e){return{tier:t,entropyScore:0,metrics:{shannonEntropy:0,lexicalDensity:0,syntacticDepth:0,noiseProbability:0},reasoning:`Forced ${t} via API options (Contextual Reply).`}}onRecordGenerated(t){return this.observers.add(t),()=>this.observers.delete(t)}getImpactMetrics(){return this.ledger.getAggregateValue()}flushRecords(){return this.ledger.exportRecords()}notifyObservers(t){this.observers.forEach(e=>{try{e(t)}catch(t){console.error("[CARNOT] Observer failed.",t)}})}};
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/core/ContextualRazor.ts","../src/types.ts","../src/core/CarnotEngine.ts","../src/core/ThermodynamicLedger.ts","../src/core/CarnotAgent.ts"],"sourcesContent":["export class ContextualRazor {\n private lastContextHash: string | null = null;\n public shave(rawPrompt: string): { optimizedPrompt: string; contextTokensSaved: number } {\n const contextRegex = /<context>([\\s\\S]*?)<\\/context>|([\\s\\S]*?)\\n\\n(?!.*\\n\\n)/;\n const match = rawPrompt.match(contextRegex);\n if (!match) return { optimizedPrompt: rawPrompt, contextTokensSaved: 0 };\n const contextBlock = match[1] || match[2] || \"\";\n const newQuery = rawPrompt.replace(match[0], \"\").trim();\n if (!contextBlock || !newQuery) return { optimizedPrompt: rawPrompt, contextTokensSaved: 0 };\n const currentHash = this.simpleHash(contextBlock);\n if (currentHash === this.lastContextHash) {\n const marker = \"[CTX_CACHED]\";\n const optimized = `${marker} ${newQuery}`;\n const tokensSaved = Math.ceil(contextBlock.length / 4);\n this.lastContextHash = currentHash;\n return { optimizedPrompt: optimized, contextTokensSaved: tokensSaved };\n }\n this.lastContextHash = currentHash;\n return { optimizedPrompt: rawPrompt, contextTokensSaved: 0 };\n }\n private simpleHash(str: string): string {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) { hash = ((hash << 5) + hash) + str.charCodeAt(i); hash = hash & hash; }\n return hash.toString(36);\n }\n}\n","export enum ComputeTier {\n LOCAL_LOGIC = 'LOCAL_LOGIC',\n EDGE_LLM = 'EDGE_LLM',\n CLOUD_GOD_TIER = 'CLOUD_GOD_TIER'\n}\nexport interface EnergyCost { dollars: number; estimatedWatts: number; }\nexport interface RoutingDecision {\n tier: ComputeTier; entropyScore: number;\n metrics: { shannonEntropy: number; lexicalDensity: number; syntacticDepth: number; noiseProbability: number; };\n reasoning: string;\n}\nexport interface ThermodynamicRecord {\n id: string; timestamp: number; promptPreview: string;\n routingDecision: RoutingDecision; legacyCost: EnergyCost; actualCost: EnergyCost; savings: EnergyCost;\n contextTokensShaved?: number;\n}\nexport type ObserverCallback = (record: ThermodynamicRecord) => void;\n","import { ComputeTier, RoutingDecision } from '../types';\n\nexport class CarnotEngine {\n private static readonly THRESHOLD_EDGE = 55;\n private static readonly THRESHOLD_CLOUD = 85;\n\n public route(prompt: string): RoutingDecision {\n const trimmedPrompt = prompt.trim();\n if (trimmedPrompt.length === 0) return this.buildDecision(0, 0, 0, 0, 0, ComputeTier.LOCAL_LOGIC, \"Empty prompt\");\n if (trimmedPrompt.length < 25) return this.buildDecision(0, 0, 0, 0, 0, ComputeTier.LOCAL_LOGIC, \"Sub-25 char. Local.\");\n\n const words = trimmedPrompt.toLowerCase().split(/[\\s,.!?;:'\"()]+/).filter(w => w.length > 0);\n \n // BYPASS MATHS : Les calculs (2+2, 5*4) ne nécessitent aucune IA\n const mathRegex = /[\\d]+\\s*[+\\-*/]\\s*[\\d]+/;\n if (mathRegex.test(trimmedPrompt) && words.length < 15) {\n return this.buildDecision(0, 0, 0, 0, 0, ComputeTier.LOCAL_LOGIC, \"Basic arithmetic. Local.\");\n }\n\n const shannon = this.calculateShannonEntropy(trimmedPrompt);\n const syntactic = this.calculateSyntacticDepth(trimmedPrompt);\n \n const spaces = (trimmedPrompt.match(/ /g) || []).length;\n const noiseProbability = trimmedPrompt.length > 20 ? Math.max(0, 1 - (spaces / (trimmedPrompt.length / 5))) : 0;\n\n // PLAFOND LEXICAL\n let lexicalRaw = new Set(words).size / Math.max(words.length, 1);\n let lexical = Math.min(lexicalRaw, 0.5);\n\n // Détecteur de Jargon (>30% de mots > 7 lettres)\n const longWords = words.filter(w => w.length > 7).length;\n if ((longWords / Math.max(words.length, 1)) > 0.3 && noiseProbability <= 0.8) {\n return this.buildDecision(100, shannon, lexical, syntactic, noiseProbability, ComputeTier.CLOUD_GOD_TIER, \"Academic/Jargon density. Forced Cloud.\");\n }\n\n let rawScore = (shannon * 0.5) + (lexical * 0.3) + (syntactic * 0.2);\n if (noiseProbability > 0.8) rawScore = rawScore * 0.1;\n\n const normalizedScore = Math.min(100, (rawScore / 3.5) * 100);\n\n let tier: ComputeTier; let reasoning: string;\n\n if (noiseProbability > 0.8) { tier = ComputeTier.LOCAL_LOGIC; reasoning = \"Gibberish detected. Blocked.\"; }\n else if (normalizedScore < CarnotEngine.THRESHOLD_EDGE) { tier = ComputeTier.LOCAL_LOGIC; reasoning = \"Low density. Local.\"; }\n else if (normalizedScore < CarnotEngine.THRESHOLD_CLOUD) { tier = ComputeTier.EDGE_LLM; reasoning = \"Moderate complexity. Edge.\"; }\n else { tier = ComputeTier.CLOUD_GOD_TIER; reasoning = \"High cognitive weight. Cloud.\"; }\n\n return this.buildDecision(normalizedScore, shannon, lexical, syntactic, noiseProbability, tier, reasoning);\n }\n\n private calculateShannonEntropy(text: string): number {\n const freq = new Map<string, number>(); const len = text.length;\n for (let i = 0; i < len; i++) { const char = text[i]; freq.set(char, (freq.get(char) || 0) + 1); }\n let entropy = 0;\n freq.forEach((count) => { const p = count / len; if (p > 0) entropy -= p * Math.log2(p); });\n return entropy;\n }\n\n private calculateSyntacticDepth(text: string): number {\n const len = text.length; if (len === 0) return 0; let depthMarkers = 0;\n for (let i = 0; i < len; i++) { \n const char = text[i]; \n if (char === ',' || char === ';' || char === '(' || char === ')' || char === ':') depthMarkers++; \n }\n return (depthMarkers / Math.max(len, 1)) * 100; \n }\n\n private buildDecision(score: number, shannon: number, lexical: number, syntactic: number, noise: number, tier: ComputeTier, reasoning: string): RoutingDecision {\n return { tier, entropyScore: Math.round(score * 100) / 100, metrics: { shannonEntropy: Math.round(shannon * 1000) / 1000, lexicalDensity: Math.round(lexical * 1000) / 1000, syntacticDepth: Math.round(syntactic * 1000) / 1000, noiseProbability: Math.round(noise * 100) / 100 }, reasoning };\n }\n}\n","import { ComputeTier, RoutingDecision, ThermodynamicRecord, EnergyCost } from '../types';\nexport class ThermodynamicLedger {\n private static readonly CLOUD_COST_PER_TOKEN_USD = 0.00001;\n private static readonly CLOUD_COST_PER_TOKEN_WATT = 0.0015;\n private static readonly EDGE_COST_MULTIPLIER = 0.05;\n private static readonly LOCAL_COST_MULTIPLIER = 0.001;\n private ledger: ThermodynamicRecord[] = [];\n private readonly MAX_BUFFER_SIZE = 1000; \n public recordDecision(prompt: string, decision: RoutingDecision): ThermodynamicRecord {\n const estimatedTokens = this.estimateTokens(prompt);\n const legacyCost = this.calculateCost(estimatedTokens, 1.0);\n let actualMultiplier = 1.0;\n if (decision.tier === ComputeTier.EDGE_LLM) actualMultiplier = ThermodynamicLedger.EDGE_COST_MULTIPLIER;\n if (decision.tier === ComputeTier.LOCAL_LOGIC) actualMultiplier = ThermodynamicLedger.LOCAL_COST_MULTIPLIER;\n const actualCost = this.calculateCost(estimatedTokens, actualMultiplier);\n const record: ThermodynamicRecord = {\n id: Math.random().toString(36).slice(2, 13), timestamp: Date.now(),\n promptPreview: prompt.substring(0, 50) + (prompt.length > 50 ? \"...\" : \"\"),\n routingDecision: decision, legacyCost, actualCost,\n savings: { dollars: legacyCost.dollars - actualCost.dollars, estimatedWatts: legacyCost.estimatedWatts - actualCost.estimatedWatts }\n };\n if (this.ledger.length >= this.MAX_BUFFER_SIZE) this.ledger.shift();\n this.ledger.push(record);\n return record;\n }\n public getAggregateValue(): { totalDollarsSaved: number; totalWattsSaved: number; requestsProcessed: number } {\n return this.ledger.reduce((acc, record) => { acc.totalDollarsSaved += record.savings.dollars; acc.totalWattsSaved += record.savings.estimatedWatts; acc.requestsProcessed += 1; return acc; }, { totalDollarsSaved: 0, totalWattsSaved: 0, requestsProcessed: 0 });\n }\n public exportRecords(): ThermodynamicRecord[] { const copy = [...this.ledger]; this.ledger = []; return copy; }\n private estimateTokens(text: string): number { return Math.ceil(text.length / 4); }\n private calculateCost(tokens: number, multiplier: number): EnergyCost {\n return { dollars: parseFloat((tokens * ThermodynamicLedger.CLOUD_COST_PER_TOKEN_USD * multiplier).toFixed(6)), estimatedWatts: parseFloat((tokens * ThermodynamicLedger.CLOUD_COST_PER_TOKEN_WATT * multiplier).toFixed(4)) };\n }\n}\n","import { ComputeTier, RoutingDecision, ThermodynamicRecord, ObserverCallback } from '../types';\nimport { ContextualRazor } from './ContextualRazor';\nimport { CarnotEngine } from './CarnotEngine';\nimport { ThermodynamicLedger } from './ThermodynamicLedger';\n\n// L'interface des options avancées (Le tuyau de contournement)\nexport interface CarnotOptions {\n forceTier?: ComputeTier;\n}\n\nexport class CarnotAgent {\n private readonly razor: ContextualRazor;\n private readonly engine: CarnotEngine;\n private readonly ledger: ThermodynamicLedger;\n private observers: Set<ObserverCallback> = new Set();\n\n constructor() {\n this.razor = new ContextualRazor();\n this.engine = new CarnotEngine();\n this.ledger = new ThermodynamicLedger();\n }\n\n // NOUVELLE SIGNATURE : On accepte maintenant les options\n /**\n * Evaluates the cognitive weight of a prompt to route it to the optimal compute tier.\n * @param rawPrompt The raw string input from the user.\n * @param options Optional overrides. Use `forceTier` to bypass heuristics and maintain conversation state with a cloud model.\n * @returns A ThermodynamicRecord containing the routing decision and estimated savings.\n */\n public execute(rawPrompt: string, options?: CarnotOptions): ThermodynamicRecord {\n const { optimizedPrompt, contextTokensSaved } = this.razor.shave(rawPrompt);\n \n // LE CONTOURNEMENT DE SÉCURITÉ :\n // Si le développeur spécifie un forceTier (parce que c'est une réponse à un Cloud),\n // on court-circuite l'Entropy Engine pour garantir le fil de la conversation.\n const decision: RoutingDecision = options?.forceTier \n ? this.buildForcedDecision(options.forceTier, optimizedPrompt)\n : this.engine.route(optimizedPrompt);\n\n const record = this.ledger.recordDecision(optimizedPrompt, decision);\n record.contextTokensShaved = contextTokensSaved;\n \n if (this.observers.size > 0) {\n Promise.resolve().then(() => this.notifyObservers(record));\n }\n\n return record;\n }\n\n // Méthode privée pour générer une décision forcée propre pour le Ledger\n private buildForcedDecision(tier: ComputeTier, prompt: string): RoutingDecision {\n return {\n tier,\n entropyScore: 0, // Non calculé\n metrics: { shannonEntropy: 0, lexicalDensity: 0, syntacticDepth: 0, noiseProbability: 0 },\n reasoning: `Forced ${tier} via API options (Contextual Reply).`\n };\n }\n\n public onRecordGenerated(callback: ObserverCallback): () => void {\n this.observers.add(callback);\n return () => this.observers.delete(callback);\n }\n\n public getImpactMetrics() {\n return this.ledger.getAggregateValue();\n }\n\n public flushRecords(): ThermodynamicRecord[] {\n return this.ledger.exportRecords();\n }\n\n private notifyObservers(record: ThermodynamicRecord): void {\n this.observers.forEach(callback => {\n try { callback(record); } \n catch (error) { console.error(\"[CARNOT] Observer failed.\", error); }\n });\n }\n}\n"],"names":["ContextualRazor","constructor","this","lastContextHash","shave","rawPrompt","match","optimizedPrompt","contextTokensSaved","contextBlock","newQuery","replace","trim","currentHash","simpleHash","optimized","tokensSaved","Math","ceil","length","str","hash","i","charCodeAt","toString","ComputeTier","CarnotEngine","route","prompt","trimmedPrompt","buildDecision","LOCAL_LOGIC","words","toLowerCase","split","filter","w","test","shannon","calculateShannonEntropy","syntactic","calculateSyntacticDepth","spaces","noiseProbability","max","lexicalRaw","Set","size","lexical","min","CLOUD_GOD_TIER","rawScore","normalizedScore","tier","reasoning","THRESHOLD_EDGE","THRESHOLD_CLOUD","EDGE_LLM","text","freq","Map","len","char","set","get","entropy","forEach","count","p","log2","depthMarkers","score","noise","entropyScore","round","metrics","shannonEntropy","lexicalDensity","syntacticDepth","ThermodynamicLedger","ledger","MAX_BUFFER_SIZE","recordDecision","decision","estimatedTokens","estimateTokens","legacyCost","calculateCost","actualMultiplier","EDGE_COST_MULTIPLIER","LOCAL_COST_MULTIPLIER","actualCost","record","id","random","slice","timestamp","Date","now","promptPreview","substring","routingDecision","savings","dollars","estimatedWatts","shift","push","getAggregateValue","reduce","acc","totalDollarsSaved","totalWattsSaved","requestsProcessed","exportRecords","copy","tokens","multiplier","parseFloat","CLOUD_COST_PER_TOKEN_USD","toFixed","CLOUD_COST_PER_TOKEN_WATT","observers","razor","engine","execute","options","forceTier","buildForcedDecision","contextTokensShaved","Promise","resolve","then","notifyObservers","onRecordGenerated","callback","add","delete","getImpactMetrics","flushRecords","error","console"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { CarnotAgent } from './core/CarnotAgent';
|
|
2
|
+
export { ComputeTier } from './types';
|
|
3
|
+
export type { ThermodynamicRecord, RoutingDecision, EnergyCost, ObserverCallback } from './types';
|
|
4
|
+
export type { CarnotOptions } from './core/CarnotAgent';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAClG,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
class t{constructor(){this.lastContextHash=null}shave(t){const e=t.match(/<context>([\s\S]*?)<\/context>|([\s\S]*?)\n\n(?!.*\n\n)/);if(!e)return{optimizedPrompt:t,contextTokensSaved:0};const s=e[1]||e[2]||"",r=t.replace(e[0],"").trim();if(!s||!r)return{optimizedPrompt:t,contextTokensSaved:0};const o=this.simpleHash(s);if(o===this.lastContextHash){const t=`[CTX_CACHED] ${r}`,e=Math.ceil(s.length/4);return this.lastContextHash=o,{optimizedPrompt:t,contextTokensSaved:e}}return this.lastContextHash=o,{optimizedPrompt:t,contextTokensSaved:0}}simpleHash(t){let e=5381;for(let s=0;s<t.length;s++)e=(e<<5)+e+t.charCodeAt(s),e&=e;return e.toString(36)}}var e;!function(t){t.LOCAL_LOGIC="LOCAL_LOGIC",t.EDGE_LLM="EDGE_LLM",t.CLOUD_GOD_TIER="CLOUD_GOD_TIER"}(e||(e={}));class s{route(t){const r=t.trim();if(0===r.length)return this.buildDecision(0,0,0,0,0,e.LOCAL_LOGIC,"Empty prompt");if(r.length<25)return this.buildDecision(0,0,0,0,0,e.LOCAL_LOGIC,"Sub-25 char. Local.");const o=r.toLowerCase().split(/[\s,.!?;:'"()]+/).filter(t=>t.length>0);if(/[\d]+\s*[+\-*/]\s*[\d]+/.test(r)&&o.length<15)return this.buildDecision(0,0,0,0,0,e.LOCAL_LOGIC,"Basic arithmetic. Local.");const i=this.calculateShannonEntropy(r),n=this.calculateSyntacticDepth(r),a=(r.match(/ /g)||[]).length,l=r.length>20?Math.max(0,1-a/(r.length/5)):0;let c=new Set(o).size/Math.max(o.length,1),h=Math.min(c,.5);if(o.filter(t=>t.length>7).length/Math.max(o.length,1)>.3&&l<=.8)return this.buildDecision(100,i,h,n,l,e.CLOUD_GOD_TIER,"Academic/Jargon density. Forced Cloud.");let d=.5*i+.3*h+.2*n;l>.8&&(d*=.1);const u=Math.min(100,d/3.5*100);let L,g;return l>.8?(L=e.LOCAL_LOGIC,g="Gibberish detected. Blocked."):u<s.THRESHOLD_EDGE?(L=e.LOCAL_LOGIC,g="Low density. Local."):u<s.THRESHOLD_CLOUD?(L=e.EDGE_LLM,g="Moderate complexity. Edge."):(L=e.CLOUD_GOD_TIER,g="High cognitive weight. Cloud."),this.buildDecision(u,i,h,n,l,L,g)}calculateShannonEntropy(t){const e=new Map,s=t.length;for(let r=0;r<s;r++){const s=t[r];e.set(s,(e.get(s)||0)+1)}let r=0;return e.forEach(t=>{const e=t/s;e>0&&(r-=e*Math.log2(e))}),r}calculateSyntacticDepth(t){const e=t.length;if(0===e)return 0;let s=0;for(let r=0;r<e;r++){const e=t[r];","!==e&&";"!==e&&"("!==e&&")"!==e&&":"!==e||s++}return s/Math.max(e,1)*100}buildDecision(t,e,s,r,o,i,n){return{tier:i,entropyScore:Math.round(100*t)/100,metrics:{shannonEntropy:Math.round(1e3*e)/1e3,lexicalDensity:Math.round(1e3*s)/1e3,syntacticDepth:Math.round(1e3*r)/1e3,noiseProbability:Math.round(100*o)/100},reasoning:n}}}s.THRESHOLD_EDGE=55,s.THRESHOLD_CLOUD=85;class r{constructor(){this.ledger=[],this.MAX_BUFFER_SIZE=1e3}recordDecision(t,s){const o=this.estimateTokens(t),i=this.calculateCost(o,1);let n=1;s.tier===e.EDGE_LLM&&(n=r.EDGE_COST_MULTIPLIER),s.tier===e.LOCAL_LOGIC&&(n=r.LOCAL_COST_MULTIPLIER);const a=this.calculateCost(o,n),l={id:Math.random().toString(36).slice(2,13),timestamp:Date.now(),promptPreview:t.substring(0,50)+(t.length>50?"...":""),routingDecision:s,legacyCost:i,actualCost:a,savings:{dollars:i.dollars-a.dollars,estimatedWatts:i.estimatedWatts-a.estimatedWatts}};return this.ledger.length>=this.MAX_BUFFER_SIZE&&this.ledger.shift(),this.ledger.push(l),l}getAggregateValue(){return this.ledger.reduce((t,e)=>(t.totalDollarsSaved+=e.savings.dollars,t.totalWattsSaved+=e.savings.estimatedWatts,t.requestsProcessed+=1,t),{totalDollarsSaved:0,totalWattsSaved:0,requestsProcessed:0})}exportRecords(){const t=[...this.ledger];return this.ledger=[],t}estimateTokens(t){return Math.ceil(t.length/4)}calculateCost(t,e){return{dollars:parseFloat((t*r.CLOUD_COST_PER_TOKEN_USD*e).toFixed(6)),estimatedWatts:parseFloat((t*r.CLOUD_COST_PER_TOKEN_WATT*e).toFixed(4))}}}r.CLOUD_COST_PER_TOKEN_USD=1e-5,r.CLOUD_COST_PER_TOKEN_WATT=.0015,r.EDGE_COST_MULTIPLIER=.05,r.LOCAL_COST_MULTIPLIER=.001;class o{constructor(){this.observers=new Set,this.razor=new t,this.engine=new s,this.ledger=new r}execute(t,e){const{optimizedPrompt:s,contextTokensSaved:r}=this.razor.shave(t),o=e?.forceTier?this.buildForcedDecision(e.forceTier,s):this.engine.route(s),i=this.ledger.recordDecision(s,o);return i.contextTokensShaved=r,this.observers.size>0&&Promise.resolve().then(()=>this.notifyObservers(i)),i}buildForcedDecision(t,e){return{tier:t,entropyScore:0,metrics:{shannonEntropy:0,lexicalDensity:0,syntacticDepth:0,noiseProbability:0},reasoning:`Forced ${t} via API options (Contextual Reply).`}}onRecordGenerated(t){return this.observers.add(t),()=>this.observers.delete(t)}getImpactMetrics(){return this.ledger.getAggregateValue()}flushRecords(){return this.ledger.exportRecords()}notifyObservers(t){this.observers.forEach(e=>{try{e(t)}catch(t){console.error("[CARNOT] Observer failed.",t)}})}}export{o as CarnotAgent,e as ComputeTier};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/core/ContextualRazor.ts","../src/types.ts","../src/core/CarnotEngine.ts","../src/core/ThermodynamicLedger.ts","../src/core/CarnotAgent.ts"],"sourcesContent":["export class ContextualRazor {\n private lastContextHash: string | null = null;\n public shave(rawPrompt: string): { optimizedPrompt: string; contextTokensSaved: number } {\n const contextRegex = /<context>([\\s\\S]*?)<\\/context>|([\\s\\S]*?)\\n\\n(?!.*\\n\\n)/;\n const match = rawPrompt.match(contextRegex);\n if (!match) return { optimizedPrompt: rawPrompt, contextTokensSaved: 0 };\n const contextBlock = match[1] || match[2] || \"\";\n const newQuery = rawPrompt.replace(match[0], \"\").trim();\n if (!contextBlock || !newQuery) return { optimizedPrompt: rawPrompt, contextTokensSaved: 0 };\n const currentHash = this.simpleHash(contextBlock);\n if (currentHash === this.lastContextHash) {\n const marker = \"[CTX_CACHED]\";\n const optimized = `${marker} ${newQuery}`;\n const tokensSaved = Math.ceil(contextBlock.length / 4);\n this.lastContextHash = currentHash;\n return { optimizedPrompt: optimized, contextTokensSaved: tokensSaved };\n }\n this.lastContextHash = currentHash;\n return { optimizedPrompt: rawPrompt, contextTokensSaved: 0 };\n }\n private simpleHash(str: string): string {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) { hash = ((hash << 5) + hash) + str.charCodeAt(i); hash = hash & hash; }\n return hash.toString(36);\n }\n}\n","export enum ComputeTier {\n LOCAL_LOGIC = 'LOCAL_LOGIC',\n EDGE_LLM = 'EDGE_LLM',\n CLOUD_GOD_TIER = 'CLOUD_GOD_TIER'\n}\nexport interface EnergyCost { dollars: number; estimatedWatts: number; }\nexport interface RoutingDecision {\n tier: ComputeTier; entropyScore: number;\n metrics: { shannonEntropy: number; lexicalDensity: number; syntacticDepth: number; noiseProbability: number; };\n reasoning: string;\n}\nexport interface ThermodynamicRecord {\n id: string; timestamp: number; promptPreview: string;\n routingDecision: RoutingDecision; legacyCost: EnergyCost; actualCost: EnergyCost; savings: EnergyCost;\n contextTokensShaved?: number;\n}\nexport type ObserverCallback = (record: ThermodynamicRecord) => void;\n","import { ComputeTier, RoutingDecision } from '../types';\n\nexport class CarnotEngine {\n private static readonly THRESHOLD_EDGE = 55;\n private static readonly THRESHOLD_CLOUD = 85;\n\n public route(prompt: string): RoutingDecision {\n const trimmedPrompt = prompt.trim();\n if (trimmedPrompt.length === 0) return this.buildDecision(0, 0, 0, 0, 0, ComputeTier.LOCAL_LOGIC, \"Empty prompt\");\n if (trimmedPrompt.length < 25) return this.buildDecision(0, 0, 0, 0, 0, ComputeTier.LOCAL_LOGIC, \"Sub-25 char. Local.\");\n\n const words = trimmedPrompt.toLowerCase().split(/[\\s,.!?;:'\"()]+/).filter(w => w.length > 0);\n \n // BYPASS MATHS : Les calculs (2+2, 5*4) ne nécessitent aucune IA\n const mathRegex = /[\\d]+\\s*[+\\-*/]\\s*[\\d]+/;\n if (mathRegex.test(trimmedPrompt) && words.length < 15) {\n return this.buildDecision(0, 0, 0, 0, 0, ComputeTier.LOCAL_LOGIC, \"Basic arithmetic. Local.\");\n }\n\n const shannon = this.calculateShannonEntropy(trimmedPrompt);\n const syntactic = this.calculateSyntacticDepth(trimmedPrompt);\n \n const spaces = (trimmedPrompt.match(/ /g) || []).length;\n const noiseProbability = trimmedPrompt.length > 20 ? Math.max(0, 1 - (spaces / (trimmedPrompt.length / 5))) : 0;\n\n // PLAFOND LEXICAL\n let lexicalRaw = new Set(words).size / Math.max(words.length, 1);\n let lexical = Math.min(lexicalRaw, 0.5);\n\n // Détecteur de Jargon (>30% de mots > 7 lettres)\n const longWords = words.filter(w => w.length > 7).length;\n if ((longWords / Math.max(words.length, 1)) > 0.3 && noiseProbability <= 0.8) {\n return this.buildDecision(100, shannon, lexical, syntactic, noiseProbability, ComputeTier.CLOUD_GOD_TIER, \"Academic/Jargon density. Forced Cloud.\");\n }\n\n let rawScore = (shannon * 0.5) + (lexical * 0.3) + (syntactic * 0.2);\n if (noiseProbability > 0.8) rawScore = rawScore * 0.1;\n\n const normalizedScore = Math.min(100, (rawScore / 3.5) * 100);\n\n let tier: ComputeTier; let reasoning: string;\n\n if (noiseProbability > 0.8) { tier = ComputeTier.LOCAL_LOGIC; reasoning = \"Gibberish detected. Blocked.\"; }\n else if (normalizedScore < CarnotEngine.THRESHOLD_EDGE) { tier = ComputeTier.LOCAL_LOGIC; reasoning = \"Low density. Local.\"; }\n else if (normalizedScore < CarnotEngine.THRESHOLD_CLOUD) { tier = ComputeTier.EDGE_LLM; reasoning = \"Moderate complexity. Edge.\"; }\n else { tier = ComputeTier.CLOUD_GOD_TIER; reasoning = \"High cognitive weight. Cloud.\"; }\n\n return this.buildDecision(normalizedScore, shannon, lexical, syntactic, noiseProbability, tier, reasoning);\n }\n\n private calculateShannonEntropy(text: string): number {\n const freq = new Map<string, number>(); const len = text.length;\n for (let i = 0; i < len; i++) { const char = text[i]; freq.set(char, (freq.get(char) || 0) + 1); }\n let entropy = 0;\n freq.forEach((count) => { const p = count / len; if (p > 0) entropy -= p * Math.log2(p); });\n return entropy;\n }\n\n private calculateSyntacticDepth(text: string): number {\n const len = text.length; if (len === 0) return 0; let depthMarkers = 0;\n for (let i = 0; i < len; i++) { \n const char = text[i]; \n if (char === ',' || char === ';' || char === '(' || char === ')' || char === ':') depthMarkers++; \n }\n return (depthMarkers / Math.max(len, 1)) * 100; \n }\n\n private buildDecision(score: number, shannon: number, lexical: number, syntactic: number, noise: number, tier: ComputeTier, reasoning: string): RoutingDecision {\n return { tier, entropyScore: Math.round(score * 100) / 100, metrics: { shannonEntropy: Math.round(shannon * 1000) / 1000, lexicalDensity: Math.round(lexical * 1000) / 1000, syntacticDepth: Math.round(syntactic * 1000) / 1000, noiseProbability: Math.round(noise * 100) / 100 }, reasoning };\n }\n}\n","import { ComputeTier, RoutingDecision, ThermodynamicRecord, EnergyCost } from '../types';\nexport class ThermodynamicLedger {\n private static readonly CLOUD_COST_PER_TOKEN_USD = 0.00001;\n private static readonly CLOUD_COST_PER_TOKEN_WATT = 0.0015;\n private static readonly EDGE_COST_MULTIPLIER = 0.05;\n private static readonly LOCAL_COST_MULTIPLIER = 0.001;\n private ledger: ThermodynamicRecord[] = [];\n private readonly MAX_BUFFER_SIZE = 1000; \n public recordDecision(prompt: string, decision: RoutingDecision): ThermodynamicRecord {\n const estimatedTokens = this.estimateTokens(prompt);\n const legacyCost = this.calculateCost(estimatedTokens, 1.0);\n let actualMultiplier = 1.0;\n if (decision.tier === ComputeTier.EDGE_LLM) actualMultiplier = ThermodynamicLedger.EDGE_COST_MULTIPLIER;\n if (decision.tier === ComputeTier.LOCAL_LOGIC) actualMultiplier = ThermodynamicLedger.LOCAL_COST_MULTIPLIER;\n const actualCost = this.calculateCost(estimatedTokens, actualMultiplier);\n const record: ThermodynamicRecord = {\n id: Math.random().toString(36).slice(2, 13), timestamp: Date.now(),\n promptPreview: prompt.substring(0, 50) + (prompt.length > 50 ? \"...\" : \"\"),\n routingDecision: decision, legacyCost, actualCost,\n savings: { dollars: legacyCost.dollars - actualCost.dollars, estimatedWatts: legacyCost.estimatedWatts - actualCost.estimatedWatts }\n };\n if (this.ledger.length >= this.MAX_BUFFER_SIZE) this.ledger.shift();\n this.ledger.push(record);\n return record;\n }\n public getAggregateValue(): { totalDollarsSaved: number; totalWattsSaved: number; requestsProcessed: number } {\n return this.ledger.reduce((acc, record) => { acc.totalDollarsSaved += record.savings.dollars; acc.totalWattsSaved += record.savings.estimatedWatts; acc.requestsProcessed += 1; return acc; }, { totalDollarsSaved: 0, totalWattsSaved: 0, requestsProcessed: 0 });\n }\n public exportRecords(): ThermodynamicRecord[] { const copy = [...this.ledger]; this.ledger = []; return copy; }\n private estimateTokens(text: string): number { return Math.ceil(text.length / 4); }\n private calculateCost(tokens: number, multiplier: number): EnergyCost {\n return { dollars: parseFloat((tokens * ThermodynamicLedger.CLOUD_COST_PER_TOKEN_USD * multiplier).toFixed(6)), estimatedWatts: parseFloat((tokens * ThermodynamicLedger.CLOUD_COST_PER_TOKEN_WATT * multiplier).toFixed(4)) };\n }\n}\n","import { ComputeTier, RoutingDecision, ThermodynamicRecord, ObserverCallback } from '../types';\nimport { ContextualRazor } from './ContextualRazor';\nimport { CarnotEngine } from './CarnotEngine';\nimport { ThermodynamicLedger } from './ThermodynamicLedger';\n\n// L'interface des options avancées (Le tuyau de contournement)\nexport interface CarnotOptions {\n forceTier?: ComputeTier;\n}\n\nexport class CarnotAgent {\n private readonly razor: ContextualRazor;\n private readonly engine: CarnotEngine;\n private readonly ledger: ThermodynamicLedger;\n private observers: Set<ObserverCallback> = new Set();\n\n constructor() {\n this.razor = new ContextualRazor();\n this.engine = new CarnotEngine();\n this.ledger = new ThermodynamicLedger();\n }\n\n // NOUVELLE SIGNATURE : On accepte maintenant les options\n /**\n * Evaluates the cognitive weight of a prompt to route it to the optimal compute tier.\n * @param rawPrompt The raw string input from the user.\n * @param options Optional overrides. Use `forceTier` to bypass heuristics and maintain conversation state with a cloud model.\n * @returns A ThermodynamicRecord containing the routing decision and estimated savings.\n */\n public execute(rawPrompt: string, options?: CarnotOptions): ThermodynamicRecord {\n const { optimizedPrompt, contextTokensSaved } = this.razor.shave(rawPrompt);\n \n // LE CONTOURNEMENT DE SÉCURITÉ :\n // Si le développeur spécifie un forceTier (parce que c'est une réponse à un Cloud),\n // on court-circuite l'Entropy Engine pour garantir le fil de la conversation.\n const decision: RoutingDecision = options?.forceTier \n ? this.buildForcedDecision(options.forceTier, optimizedPrompt)\n : this.engine.route(optimizedPrompt);\n\n const record = this.ledger.recordDecision(optimizedPrompt, decision);\n record.contextTokensShaved = contextTokensSaved;\n \n if (this.observers.size > 0) {\n Promise.resolve().then(() => this.notifyObservers(record));\n }\n\n return record;\n }\n\n // Méthode privée pour générer une décision forcée propre pour le Ledger\n private buildForcedDecision(tier: ComputeTier, prompt: string): RoutingDecision {\n return {\n tier,\n entropyScore: 0, // Non calculé\n metrics: { shannonEntropy: 0, lexicalDensity: 0, syntacticDepth: 0, noiseProbability: 0 },\n reasoning: `Forced ${tier} via API options (Contextual Reply).`\n };\n }\n\n public onRecordGenerated(callback: ObserverCallback): () => void {\n this.observers.add(callback);\n return () => this.observers.delete(callback);\n }\n\n public getImpactMetrics() {\n return this.ledger.getAggregateValue();\n }\n\n public flushRecords(): ThermodynamicRecord[] {\n return this.ledger.exportRecords();\n }\n\n private notifyObservers(record: ThermodynamicRecord): void {\n this.observers.forEach(callback => {\n try { callback(record); } \n catch (error) { console.error(\"[CARNOT] Observer failed.\", error); }\n });\n }\n}\n"],"names":["ContextualRazor","constructor","this","lastContextHash","shave","rawPrompt","match","optimizedPrompt","contextTokensSaved","contextBlock","newQuery","replace","trim","currentHash","simpleHash","optimized","tokensSaved","Math","ceil","length","str","hash","i","charCodeAt","toString","ComputeTier","CarnotEngine","route","prompt","trimmedPrompt","buildDecision","LOCAL_LOGIC","words","toLowerCase","split","filter","w","test","shannon","calculateShannonEntropy","syntactic","calculateSyntacticDepth","spaces","noiseProbability","max","lexicalRaw","Set","size","lexical","min","CLOUD_GOD_TIER","rawScore","normalizedScore","tier","reasoning","THRESHOLD_EDGE","THRESHOLD_CLOUD","EDGE_LLM","text","freq","Map","len","char","set","get","entropy","forEach","count","p","log2","depthMarkers","score","noise","entropyScore","round","metrics","shannonEntropy","lexicalDensity","syntacticDepth","ThermodynamicLedger","ledger","MAX_BUFFER_SIZE","recordDecision","decision","estimatedTokens","estimateTokens","legacyCost","calculateCost","actualMultiplier","EDGE_COST_MULTIPLIER","LOCAL_COST_MULTIPLIER","actualCost","record","id","random","slice","timestamp","Date","now","promptPreview","substring","routingDecision","savings","dollars","estimatedWatts","shift","push","getAggregateValue","reduce","acc","totalDollarsSaved","totalWattsSaved","requestsProcessed","exportRecords","copy","tokens","multiplier","parseFloat","CLOUD_COST_PER_TOKEN_USD","toFixed","CLOUD_COST_PER_TOKEN_WATT","CarnotAgent","observers","razor","engine","execute","options","forceTier","buildForcedDecision","contextTokensShaved","Promise","resolve","then","notifyObservers","onRecordGenerated","callback","add","delete","getImpactMetrics","flushRecords","error","console"],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare enum ComputeTier {
|
|
2
|
+
LOCAL_LOGIC = "LOCAL_LOGIC",
|
|
3
|
+
EDGE_LLM = "EDGE_LLM",
|
|
4
|
+
CLOUD_GOD_TIER = "CLOUD_GOD_TIER"
|
|
5
|
+
}
|
|
6
|
+
export interface EnergyCost {
|
|
7
|
+
dollars: number;
|
|
8
|
+
estimatedWatts: number;
|
|
9
|
+
}
|
|
10
|
+
export interface RoutingDecision {
|
|
11
|
+
tier: ComputeTier;
|
|
12
|
+
entropyScore: number;
|
|
13
|
+
metrics: {
|
|
14
|
+
shannonEntropy: number;
|
|
15
|
+
lexicalDensity: number;
|
|
16
|
+
syntacticDepth: number;
|
|
17
|
+
noiseProbability: number;
|
|
18
|
+
};
|
|
19
|
+
reasoning: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ThermodynamicRecord {
|
|
22
|
+
id: string;
|
|
23
|
+
timestamp: number;
|
|
24
|
+
promptPreview: string;
|
|
25
|
+
routingDecision: RoutingDecision;
|
|
26
|
+
legacyCost: EnergyCost;
|
|
27
|
+
actualCost: EnergyCost;
|
|
28
|
+
savings: EnergyCost;
|
|
29
|
+
contextTokensShaved?: number;
|
|
30
|
+
}
|
|
31
|
+
export type ObserverCallback = (record: ThermodynamicRecord) => void;
|
|
32
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,oBAAY,WAAW;IACnB,WAAW,gBAAgB;IAC3B,QAAQ,aAAa;IACrB,cAAc,mBAAmB;CACpC;AACD,MAAM,WAAW,UAAU;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;CAAE;AACxE,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,WAAW,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IACxC,OAAO,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;KAAE,CAAC;IAC/G,SAAS,EAAE,MAAM,CAAC;CACrB;AACD,MAAM,WAAW,mBAAmB;IAChC,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IACrD,eAAe,EAAE,eAAe,CAAC;IAAC,UAAU,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,UAAU,CAAC;IACtG,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAChC;AACD,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "carnot-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Zero-latency edge routing for LLMs using Shannon entropy.",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": { "types": "./dist/index.d.ts", "default": "./dist/index.mjs" },
|
|
11
|
+
"require": { "types": "./dist/index.d.ts", "default": "./dist/index.cjs" }
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": ["dist", "src", "README.md", "LICENSE"],
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "rollup -c rollup.config.mjs && tsc --emitDeclarationOnly"
|
|
18
|
+
},
|
|
19
|
+
"keywords": ["ai", "llm", "edge-computing", "shannon-entropy", "optimization", "cost-reduction", "green-ai", "open-source"],
|
|
20
|
+
"author": "Fleury99",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/Fleury99/carnot-sdk"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
28
|
+
"@rollup/plugin-typescript": "^11.1.6",
|
|
29
|
+
"rollup": "^4.9.6",
|
|
30
|
+
"typescript": "^5.3.3"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ComputeTier, RoutingDecision, ThermodynamicRecord, ObserverCallback } from '../types';
|
|
2
|
+
import { ContextualRazor } from './ContextualRazor';
|
|
3
|
+
import { CarnotEngine } from './CarnotEngine';
|
|
4
|
+
import { ThermodynamicLedger } from './ThermodynamicLedger';
|
|
5
|
+
|
|
6
|
+
// L'interface des options avancées (Le tuyau de contournement)
|
|
7
|
+
export interface CarnotOptions {
|
|
8
|
+
forceTier?: ComputeTier;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class CarnotAgent {
|
|
12
|
+
private readonly razor: ContextualRazor;
|
|
13
|
+
private readonly engine: CarnotEngine;
|
|
14
|
+
private readonly ledger: ThermodynamicLedger;
|
|
15
|
+
private observers: Set<ObserverCallback> = new Set();
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
this.razor = new ContextualRazor();
|
|
19
|
+
this.engine = new CarnotEngine();
|
|
20
|
+
this.ledger = new ThermodynamicLedger();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// NOUVELLE SIGNATURE : On accepte maintenant les options
|
|
24
|
+
/**
|
|
25
|
+
* Evaluates the cognitive weight of a prompt to route it to the optimal compute tier.
|
|
26
|
+
* @param rawPrompt The raw string input from the user.
|
|
27
|
+
* @param options Optional overrides. Use `forceTier` to bypass heuristics and maintain conversation state with a cloud model.
|
|
28
|
+
* @returns A ThermodynamicRecord containing the routing decision and estimated savings.
|
|
29
|
+
*/
|
|
30
|
+
public execute(rawPrompt: string, options?: CarnotOptions): ThermodynamicRecord {
|
|
31
|
+
const { optimizedPrompt, contextTokensSaved } = this.razor.shave(rawPrompt);
|
|
32
|
+
|
|
33
|
+
// LE CONTOURNEMENT DE SÉCURITÉ :
|
|
34
|
+
// Si le développeur spécifie un forceTier (parce que c'est une réponse à un Cloud),
|
|
35
|
+
// on court-circuite l'Entropy Engine pour garantir le fil de la conversation.
|
|
36
|
+
const decision: RoutingDecision = options?.forceTier
|
|
37
|
+
? this.buildForcedDecision(options.forceTier, optimizedPrompt)
|
|
38
|
+
: this.engine.route(optimizedPrompt);
|
|
39
|
+
|
|
40
|
+
const record = this.ledger.recordDecision(optimizedPrompt, decision);
|
|
41
|
+
record.contextTokensShaved = contextTokensSaved;
|
|
42
|
+
|
|
43
|
+
if (this.observers.size > 0) {
|
|
44
|
+
Promise.resolve().then(() => this.notifyObservers(record));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return record;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Méthode privée pour générer une décision forcée propre pour le Ledger
|
|
51
|
+
private buildForcedDecision(tier: ComputeTier, prompt: string): RoutingDecision {
|
|
52
|
+
return {
|
|
53
|
+
tier,
|
|
54
|
+
entropyScore: 0, // Non calculé
|
|
55
|
+
metrics: { shannonEntropy: 0, lexicalDensity: 0, syntacticDepth: 0, noiseProbability: 0 },
|
|
56
|
+
reasoning: `Forced ${tier} via API options (Contextual Reply).`
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public onRecordGenerated(callback: ObserverCallback): () => void {
|
|
61
|
+
this.observers.add(callback);
|
|
62
|
+
return () => this.observers.delete(callback);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public getImpactMetrics() {
|
|
66
|
+
return this.ledger.getAggregateValue();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public flushRecords(): ThermodynamicRecord[] {
|
|
70
|
+
return this.ledger.exportRecords();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private notifyObservers(record: ThermodynamicRecord): void {
|
|
74
|
+
this.observers.forEach(callback => {
|
|
75
|
+
try { callback(record); }
|
|
76
|
+
catch (error) { console.error("[CARNOT] Observer failed.", error); }
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ComputeTier, RoutingDecision } from '../types';
|
|
2
|
+
|
|
3
|
+
export class CarnotEngine {
|
|
4
|
+
private static readonly THRESHOLD_EDGE = 55;
|
|
5
|
+
private static readonly THRESHOLD_CLOUD = 85;
|
|
6
|
+
|
|
7
|
+
public route(prompt: string): RoutingDecision {
|
|
8
|
+
const trimmedPrompt = prompt.trim();
|
|
9
|
+
if (trimmedPrompt.length === 0) return this.buildDecision(0, 0, 0, 0, 0, ComputeTier.LOCAL_LOGIC, "Empty prompt");
|
|
10
|
+
if (trimmedPrompt.length < 25) return this.buildDecision(0, 0, 0, 0, 0, ComputeTier.LOCAL_LOGIC, "Sub-25 char. Local.");
|
|
11
|
+
|
|
12
|
+
const words = trimmedPrompt.toLowerCase().split(/[\s,.!?;:'"()]+/).filter(w => w.length > 0);
|
|
13
|
+
|
|
14
|
+
// BYPASS MATHS : Les calculs (2+2, 5*4) ne nécessitent aucune IA
|
|
15
|
+
const mathRegex = /[\d]+\s*[+\-*/]\s*[\d]+/;
|
|
16
|
+
if (mathRegex.test(trimmedPrompt) && words.length < 15) {
|
|
17
|
+
return this.buildDecision(0, 0, 0, 0, 0, ComputeTier.LOCAL_LOGIC, "Basic arithmetic. Local.");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const shannon = this.calculateShannonEntropy(trimmedPrompt);
|
|
21
|
+
const syntactic = this.calculateSyntacticDepth(trimmedPrompt);
|
|
22
|
+
|
|
23
|
+
const spaces = (trimmedPrompt.match(/ /g) || []).length;
|
|
24
|
+
const noiseProbability = trimmedPrompt.length > 20 ? Math.max(0, 1 - (spaces / (trimmedPrompt.length / 5))) : 0;
|
|
25
|
+
|
|
26
|
+
// PLAFOND LEXICAL
|
|
27
|
+
let lexicalRaw = new Set(words).size / Math.max(words.length, 1);
|
|
28
|
+
let lexical = Math.min(lexicalRaw, 0.5);
|
|
29
|
+
|
|
30
|
+
// Détecteur de Jargon (>30% de mots > 7 lettres)
|
|
31
|
+
const longWords = words.filter(w => w.length > 7).length;
|
|
32
|
+
if ((longWords / Math.max(words.length, 1)) > 0.3 && noiseProbability <= 0.8) {
|
|
33
|
+
return this.buildDecision(100, shannon, lexical, syntactic, noiseProbability, ComputeTier.CLOUD_GOD_TIER, "Academic/Jargon density. Forced Cloud.");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let rawScore = (shannon * 0.5) + (lexical * 0.3) + (syntactic * 0.2);
|
|
37
|
+
if (noiseProbability > 0.8) rawScore = rawScore * 0.1;
|
|
38
|
+
|
|
39
|
+
const normalizedScore = Math.min(100, (rawScore / 3.5) * 100);
|
|
40
|
+
|
|
41
|
+
let tier: ComputeTier; let reasoning: string;
|
|
42
|
+
|
|
43
|
+
if (noiseProbability > 0.8) { tier = ComputeTier.LOCAL_LOGIC; reasoning = "Gibberish detected. Blocked."; }
|
|
44
|
+
else if (normalizedScore < CarnotEngine.THRESHOLD_EDGE) { tier = ComputeTier.LOCAL_LOGIC; reasoning = "Low density. Local."; }
|
|
45
|
+
else if (normalizedScore < CarnotEngine.THRESHOLD_CLOUD) { tier = ComputeTier.EDGE_LLM; reasoning = "Moderate complexity. Edge."; }
|
|
46
|
+
else { tier = ComputeTier.CLOUD_GOD_TIER; reasoning = "High cognitive weight. Cloud."; }
|
|
47
|
+
|
|
48
|
+
return this.buildDecision(normalizedScore, shannon, lexical, syntactic, noiseProbability, tier, reasoning);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private calculateShannonEntropy(text: string): number {
|
|
52
|
+
const freq = new Map<string, number>(); const len = text.length;
|
|
53
|
+
for (let i = 0; i < len; i++) { const char = text[i]; freq.set(char, (freq.get(char) || 0) + 1); }
|
|
54
|
+
let entropy = 0;
|
|
55
|
+
freq.forEach((count) => { const p = count / len; if (p > 0) entropy -= p * Math.log2(p); });
|
|
56
|
+
return entropy;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private calculateSyntacticDepth(text: string): number {
|
|
60
|
+
const len = text.length; if (len === 0) return 0; let depthMarkers = 0;
|
|
61
|
+
for (let i = 0; i < len; i++) {
|
|
62
|
+
const char = text[i];
|
|
63
|
+
if (char === ',' || char === ';' || char === '(' || char === ')' || char === ':') depthMarkers++;
|
|
64
|
+
}
|
|
65
|
+
return (depthMarkers / Math.max(len, 1)) * 100;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private buildDecision(score: number, shannon: number, lexical: number, syntactic: number, noise: number, tier: ComputeTier, reasoning: string): RoutingDecision {
|
|
69
|
+
return { tier, entropyScore: Math.round(score * 100) / 100, metrics: { shannonEntropy: Math.round(shannon * 1000) / 1000, lexicalDensity: Math.round(lexical * 1000) / 1000, syntacticDepth: Math.round(syntactic * 1000) / 1000, noiseProbability: Math.round(noise * 100) / 100 }, reasoning };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export class ContextualRazor {
|
|
2
|
+
private lastContextHash: string | null = null;
|
|
3
|
+
public shave(rawPrompt: string): { optimizedPrompt: string; contextTokensSaved: number } {
|
|
4
|
+
const contextRegex = /<context>([\s\S]*?)<\/context>|([\s\S]*?)\n\n(?!.*\n\n)/;
|
|
5
|
+
const match = rawPrompt.match(contextRegex);
|
|
6
|
+
if (!match) return { optimizedPrompt: rawPrompt, contextTokensSaved: 0 };
|
|
7
|
+
const contextBlock = match[1] || match[2] || "";
|
|
8
|
+
const newQuery = rawPrompt.replace(match[0], "").trim();
|
|
9
|
+
if (!contextBlock || !newQuery) return { optimizedPrompt: rawPrompt, contextTokensSaved: 0 };
|
|
10
|
+
const currentHash = this.simpleHash(contextBlock);
|
|
11
|
+
if (currentHash === this.lastContextHash) {
|
|
12
|
+
const marker = "[CTX_CACHED]";
|
|
13
|
+
const optimized = `${marker} ${newQuery}`;
|
|
14
|
+
const tokensSaved = Math.ceil(contextBlock.length / 4);
|
|
15
|
+
this.lastContextHash = currentHash;
|
|
16
|
+
return { optimizedPrompt: optimized, contextTokensSaved: tokensSaved };
|
|
17
|
+
}
|
|
18
|
+
this.lastContextHash = currentHash;
|
|
19
|
+
return { optimizedPrompt: rawPrompt, contextTokensSaved: 0 };
|
|
20
|
+
}
|
|
21
|
+
private simpleHash(str: string): string {
|
|
22
|
+
let hash = 5381;
|
|
23
|
+
for (let i = 0; i < str.length; i++) { hash = ((hash << 5) + hash) + str.charCodeAt(i); hash = hash & hash; }
|
|
24
|
+
return hash.toString(36);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ComputeTier, RoutingDecision, ThermodynamicRecord, EnergyCost } from '../types';
|
|
2
|
+
export class ThermodynamicLedger {
|
|
3
|
+
private static readonly CLOUD_COST_PER_TOKEN_USD = 0.00001;
|
|
4
|
+
private static readonly CLOUD_COST_PER_TOKEN_WATT = 0.0015;
|
|
5
|
+
private static readonly EDGE_COST_MULTIPLIER = 0.05;
|
|
6
|
+
private static readonly LOCAL_COST_MULTIPLIER = 0.001;
|
|
7
|
+
private ledger: ThermodynamicRecord[] = [];
|
|
8
|
+
private readonly MAX_BUFFER_SIZE = 1000;
|
|
9
|
+
public recordDecision(prompt: string, decision: RoutingDecision): ThermodynamicRecord {
|
|
10
|
+
const estimatedTokens = this.estimateTokens(prompt);
|
|
11
|
+
const legacyCost = this.calculateCost(estimatedTokens, 1.0);
|
|
12
|
+
let actualMultiplier = 1.0;
|
|
13
|
+
if (decision.tier === ComputeTier.EDGE_LLM) actualMultiplier = ThermodynamicLedger.EDGE_COST_MULTIPLIER;
|
|
14
|
+
if (decision.tier === ComputeTier.LOCAL_LOGIC) actualMultiplier = ThermodynamicLedger.LOCAL_COST_MULTIPLIER;
|
|
15
|
+
const actualCost = this.calculateCost(estimatedTokens, actualMultiplier);
|
|
16
|
+
const record: ThermodynamicRecord = {
|
|
17
|
+
id: Math.random().toString(36).slice(2, 13), timestamp: Date.now(),
|
|
18
|
+
promptPreview: prompt.substring(0, 50) + (prompt.length > 50 ? "..." : ""),
|
|
19
|
+
routingDecision: decision, legacyCost, actualCost,
|
|
20
|
+
savings: { dollars: legacyCost.dollars - actualCost.dollars, estimatedWatts: legacyCost.estimatedWatts - actualCost.estimatedWatts }
|
|
21
|
+
};
|
|
22
|
+
if (this.ledger.length >= this.MAX_BUFFER_SIZE) this.ledger.shift();
|
|
23
|
+
this.ledger.push(record);
|
|
24
|
+
return record;
|
|
25
|
+
}
|
|
26
|
+
public getAggregateValue(): { totalDollarsSaved: number; totalWattsSaved: number; requestsProcessed: number } {
|
|
27
|
+
return this.ledger.reduce((acc, record) => { acc.totalDollarsSaved += record.savings.dollars; acc.totalWattsSaved += record.savings.estimatedWatts; acc.requestsProcessed += 1; return acc; }, { totalDollarsSaved: 0, totalWattsSaved: 0, requestsProcessed: 0 });
|
|
28
|
+
}
|
|
29
|
+
public exportRecords(): ThermodynamicRecord[] { const copy = [...this.ledger]; this.ledger = []; return copy; }
|
|
30
|
+
private estimateTokens(text: string): number { return Math.ceil(text.length / 4); }
|
|
31
|
+
private calculateCost(tokens: number, multiplier: number): EnergyCost {
|
|
32
|
+
return { dollars: parseFloat((tokens * ThermodynamicLedger.CLOUD_COST_PER_TOKEN_USD * multiplier).toFixed(6)), estimatedWatts: parseFloat((tokens * ThermodynamicLedger.CLOUD_COST_PER_TOKEN_WATT * multiplier).toFixed(4)) };
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export enum ComputeTier {
|
|
2
|
+
LOCAL_LOGIC = 'LOCAL_LOGIC',
|
|
3
|
+
EDGE_LLM = 'EDGE_LLM',
|
|
4
|
+
CLOUD_GOD_TIER = 'CLOUD_GOD_TIER'
|
|
5
|
+
}
|
|
6
|
+
export interface EnergyCost { dollars: number; estimatedWatts: number; }
|
|
7
|
+
export interface RoutingDecision {
|
|
8
|
+
tier: ComputeTier; entropyScore: number;
|
|
9
|
+
metrics: { shannonEntropy: number; lexicalDensity: number; syntacticDepth: number; noiseProbability: number; };
|
|
10
|
+
reasoning: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ThermodynamicRecord {
|
|
13
|
+
id: string; timestamp: number; promptPreview: string;
|
|
14
|
+
routingDecision: RoutingDecision; legacyCost: EnergyCost; actualCost: EnergyCost; savings: EnergyCost;
|
|
15
|
+
contextTokensShaved?: number;
|
|
16
|
+
}
|
|
17
|
+
export type ObserverCallback = (record: ThermodynamicRecord) => void;
|