@kortexya/reasoninglayer 0.2.1 → 0.2.3
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/README.md +9 -3
- package/dist/index.cjs +559 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +464 -2
- package/dist/index.d.ts +464 -2
- package/dist/index.js +559 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,8 +7,8 @@ TypeScript SDK for the [Reasoning Layer](https://kortexya.com) API — a knowled
|
|
|
7
7
|
- **Zero runtime dependencies** — only `fetch` and `AbortController` (ES2022)
|
|
8
8
|
- **Dual ESM/CJS** — works in Node 18+, modern browsers, Deno, and Bun
|
|
9
9
|
- **Full type safety** — strict TypeScript with discriminated unions, no `any` types
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
10
|
+
- **29 resource clients** covering sorts, terms, inference, query, fuzzy logic, cognitive agents, causal reasoning, optimization, and more
|
|
11
|
+
- **8 builder namespaces** — `Value`, `FeatureInput`, `TermInput`, `guard`, `SortBuilder`, `psi`, `allen`, `LP`
|
|
12
12
|
- **Automatic retry** — exponential backoff with jitter on 429 and 503 responses
|
|
13
13
|
- **WebSocket** — real-time cognitive agent event subscriptions with auto-reconnect
|
|
14
14
|
- **Error hierarchy** — typed errors for constraint violations, rate limits, timeouts, and network failures
|
|
@@ -107,6 +107,7 @@ console.log(result.solutions); // Solutions with bindings
|
|
|
107
107
|
| `actionReviews` | Action reviews | approve, reject, modify autonomous actions |
|
|
108
108
|
| `discovery` | Discovery | causal effect discovery, prediction |
|
|
109
109
|
| `extract` | Extraction | entity extraction from text |
|
|
110
|
+
| `optimize` | Optimization | LP solve, KB-driven optimization |
|
|
110
111
|
|
|
111
112
|
## Configuration
|
|
112
113
|
|
|
@@ -167,7 +168,7 @@ try {
|
|
|
167
168
|
The SDK provides builder functions for constructing API request values with full type safety:
|
|
168
169
|
|
|
169
170
|
```typescript
|
|
170
|
-
import { Value, FeatureInput, TermInput, guard, allen, SortBuilder, psi } from '@kortexya/reasoninglayer';
|
|
171
|
+
import { Value, FeatureInput, TermInput, guard, allen, SortBuilder, psi, LP } from '@kortexya/reasoninglayer';
|
|
171
172
|
|
|
172
173
|
// Tagged values (term CRUD)
|
|
173
174
|
Value.string('hello') // { type: 'String', value: 'hello' }
|
|
@@ -185,6 +186,11 @@ guard('gt', 100) // { guard: { op: 'gt', value: 100 } }
|
|
|
185
186
|
// Allen temporal relations
|
|
186
187
|
allen('before') // { allen: 'before' }
|
|
187
188
|
|
|
189
|
+
// LP optimization
|
|
190
|
+
LP.maximize({ x: 3, y: 5 }) // objective function
|
|
191
|
+
LP.constraint({ x: 1, y: 3 }, '<=', 12) // linear constraint
|
|
192
|
+
LP.nonNegative('x', 'y') // variable bounds
|
|
193
|
+
|
|
188
194
|
// Fluent sort builder
|
|
189
195
|
new SortBuilder('employee')
|
|
190
196
|
.parents(['person'])
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// src/config.ts
|
|
4
|
-
var SDK_VERSION = "0.2.
|
|
4
|
+
var SDK_VERSION = "0.2.3";
|
|
5
5
|
function resolveConfig(config) {
|
|
6
6
|
if (!config.baseUrl) {
|
|
7
7
|
throw new Error("ClientConfig.baseUrl is required");
|
|
@@ -12548,6 +12548,560 @@ var RagClient = class {
|
|
|
12548
12548
|
}
|
|
12549
12549
|
};
|
|
12550
12550
|
|
|
12551
|
+
// src/builders/lp.ts
|
|
12552
|
+
function numericTerm(n) {
|
|
12553
|
+
return {
|
|
12554
|
+
sort_name: Number.isInteger(n) ? "integer_type" : "real_type",
|
|
12555
|
+
features: { value: n }
|
|
12556
|
+
};
|
|
12557
|
+
}
|
|
12558
|
+
var LP = {
|
|
12559
|
+
/**
|
|
12560
|
+
* Create a maximization objective.
|
|
12561
|
+
*
|
|
12562
|
+
* @param coefficients - Map of variable names to their objective coefficients.
|
|
12563
|
+
* @returns An objective function to maximize.
|
|
12564
|
+
*
|
|
12565
|
+
* @remarks Serialization format: SDK-internal (compiled to constraint sorts).
|
|
12566
|
+
*
|
|
12567
|
+
* @example
|
|
12568
|
+
* ```typescript
|
|
12569
|
+
* LP.maximize({ chairs: 3, tables: 5 })
|
|
12570
|
+
* // { direction: 'maximize', coefficients: { chairs: 3, tables: 5 } }
|
|
12571
|
+
* ```
|
|
12572
|
+
*/
|
|
12573
|
+
maximize(coefficients) {
|
|
12574
|
+
return { direction: "maximize", coefficients };
|
|
12575
|
+
},
|
|
12576
|
+
/**
|
|
12577
|
+
* Create a minimization objective.
|
|
12578
|
+
*
|
|
12579
|
+
* @param coefficients - Map of variable names to their objective coefficients.
|
|
12580
|
+
* @returns An objective function to minimize.
|
|
12581
|
+
*
|
|
12582
|
+
* @remarks Serialization format: SDK-internal (compiled to constraint sorts).
|
|
12583
|
+
*
|
|
12584
|
+
* @example
|
|
12585
|
+
* ```typescript
|
|
12586
|
+
* LP.minimize({ cost_a: 2, cost_b: 5 })
|
|
12587
|
+
* // { direction: 'minimize', coefficients: { cost_a: 2, cost_b: 5 } }
|
|
12588
|
+
* ```
|
|
12589
|
+
*/
|
|
12590
|
+
minimize(coefficients) {
|
|
12591
|
+
return { direction: "minimize", coefficients };
|
|
12592
|
+
},
|
|
12593
|
+
/**
|
|
12594
|
+
* Create a linear constraint.
|
|
12595
|
+
*
|
|
12596
|
+
* @param coefficients - Map of variable names to their constraint coefficients.
|
|
12597
|
+
* @param op - Comparison operator: '<=', '>=', or '='.
|
|
12598
|
+
* @param rhs - Right-hand side constant.
|
|
12599
|
+
* @param label - Optional human-readable label.
|
|
12600
|
+
* @returns A linear constraint.
|
|
12601
|
+
*
|
|
12602
|
+
* @remarks Serialization format: SDK-internal (compiled to constraint sorts).
|
|
12603
|
+
*
|
|
12604
|
+
* @example
|
|
12605
|
+
* ```typescript
|
|
12606
|
+
* LP.constraint({ chairs: 1, tables: 3 }, '<=', 12, 'wood')
|
|
12607
|
+
* // { coefficients: { chairs: 1, tables: 3 }, op: '<=', rhs: 12, label: 'wood' }
|
|
12608
|
+
* ```
|
|
12609
|
+
*/
|
|
12610
|
+
constraint(coefficients, op, rhs, label) {
|
|
12611
|
+
return label !== void 0 ? { coefficients, op, rhs, label } : { coefficients, op, rhs };
|
|
12612
|
+
},
|
|
12613
|
+
/**
|
|
12614
|
+
* Create non-negativity bounds (>= 0) for the given variables.
|
|
12615
|
+
*
|
|
12616
|
+
* @param variables - Variable names to constrain to be non-negative.
|
|
12617
|
+
* @returns A bounds map with `{ min: 0 }` for each variable.
|
|
12618
|
+
*
|
|
12619
|
+
* @remarks Serialization format: SDK-internal (compiled to constraint sorts).
|
|
12620
|
+
*
|
|
12621
|
+
* @example
|
|
12622
|
+
* ```typescript
|
|
12623
|
+
* LP.nonNegative('chairs', 'tables')
|
|
12624
|
+
* // { chairs: { min: 0 }, tables: { min: 0 } }
|
|
12625
|
+
* ```
|
|
12626
|
+
*/
|
|
12627
|
+
nonNegative(...variables) {
|
|
12628
|
+
const bounds = {};
|
|
12629
|
+
for (const v of variables) {
|
|
12630
|
+
bounds[v] = { min: 0 };
|
|
12631
|
+
}
|
|
12632
|
+
return bounds;
|
|
12633
|
+
},
|
|
12634
|
+
/**
|
|
12635
|
+
* Create bounds for a variable.
|
|
12636
|
+
*
|
|
12637
|
+
* @param min - Lower bound (omit for unbounded below).
|
|
12638
|
+
* @param max - Upper bound (omit for unbounded above).
|
|
12639
|
+
* @returns Variable bounds specification.
|
|
12640
|
+
*
|
|
12641
|
+
* @remarks Serialization format: SDK-internal (compiled to constraint sorts).
|
|
12642
|
+
*
|
|
12643
|
+
* @example
|
|
12644
|
+
* ```typescript
|
|
12645
|
+
* LP.bounds(0, 100)
|
|
12646
|
+
* // { min: 0, max: 100 }
|
|
12647
|
+
* ```
|
|
12648
|
+
*/
|
|
12649
|
+
bounds(min, max) {
|
|
12650
|
+
const b = {};
|
|
12651
|
+
if (min !== void 0) b.min = min;
|
|
12652
|
+
if (max !== void 0) b.max = max;
|
|
12653
|
+
return b;
|
|
12654
|
+
}
|
|
12655
|
+
};
|
|
12656
|
+
function compileLP(problem, solutionSortName) {
|
|
12657
|
+
const counter = { value: 0 };
|
|
12658
|
+
const nextVar = () => `?_lp_${counter.value++}`;
|
|
12659
|
+
const variableNames = discoverVariables(problem);
|
|
12660
|
+
if (variableNames.length === 0) {
|
|
12661
|
+
throw new Error("LP problem has no variables. At least one variable must appear in the objective or constraints.");
|
|
12662
|
+
}
|
|
12663
|
+
const varRefs = {};
|
|
12664
|
+
for (const name of variableNames) {
|
|
12665
|
+
varRefs[name] = { name: `?${name}` };
|
|
12666
|
+
}
|
|
12667
|
+
const allAntecedents = [];
|
|
12668
|
+
if (problem.bounds) {
|
|
12669
|
+
for (const [varName, bounds] of Object.entries(problem.bounds)) {
|
|
12670
|
+
if (!(varName in varRefs)) continue;
|
|
12671
|
+
const ref = varRefs[varName];
|
|
12672
|
+
if (bounds.min !== void 0 && bounds.max !== void 0) {
|
|
12673
|
+
allAntecedents.push({
|
|
12674
|
+
sort_name: "real_between_constraint",
|
|
12675
|
+
features: {
|
|
12676
|
+
var: ref,
|
|
12677
|
+
lower: numericTerm(bounds.min),
|
|
12678
|
+
upper: numericTerm(bounds.max)
|
|
12679
|
+
}
|
|
12680
|
+
});
|
|
12681
|
+
} else {
|
|
12682
|
+
if (bounds.min !== void 0) {
|
|
12683
|
+
allAntecedents.push({
|
|
12684
|
+
sort_name: "real_ge_constraint",
|
|
12685
|
+
features: {
|
|
12686
|
+
left: ref,
|
|
12687
|
+
right: numericTerm(bounds.min)
|
|
12688
|
+
}
|
|
12689
|
+
});
|
|
12690
|
+
}
|
|
12691
|
+
if (bounds.max !== void 0) {
|
|
12692
|
+
allAntecedents.push({
|
|
12693
|
+
sort_name: "real_le_constraint",
|
|
12694
|
+
features: {
|
|
12695
|
+
left: ref,
|
|
12696
|
+
right: numericTerm(bounds.max)
|
|
12697
|
+
}
|
|
12698
|
+
});
|
|
12699
|
+
}
|
|
12700
|
+
}
|
|
12701
|
+
}
|
|
12702
|
+
}
|
|
12703
|
+
for (const constraint of problem.constraints) {
|
|
12704
|
+
const compiled = compileLinearExpression(constraint.coefficients, varRefs, nextVar);
|
|
12705
|
+
allAntecedents.push(...compiled.antecedents);
|
|
12706
|
+
const sortName = constraintOpToSort(constraint.op);
|
|
12707
|
+
allAntecedents.push({
|
|
12708
|
+
sort_name: sortName,
|
|
12709
|
+
features: {
|
|
12710
|
+
left: compiled.resultRef,
|
|
12711
|
+
right: numericTerm(constraint.rhs)
|
|
12712
|
+
}
|
|
12713
|
+
});
|
|
12714
|
+
}
|
|
12715
|
+
const objectiveCompiled = compileLinearExpression(
|
|
12716
|
+
problem.objective.coefficients,
|
|
12717
|
+
varRefs,
|
|
12718
|
+
nextVar
|
|
12719
|
+
);
|
|
12720
|
+
allAntecedents.push(...objectiveCompiled.antecedents);
|
|
12721
|
+
let objectiveRef = objectiveCompiled.resultRef;
|
|
12722
|
+
if (typeof objectiveRef === "number") {
|
|
12723
|
+
const objVar = { name: "?_lp_objective" };
|
|
12724
|
+
allAntecedents.push({
|
|
12725
|
+
sort_name: "real_eq_constraint",
|
|
12726
|
+
features: {
|
|
12727
|
+
left: objVar,
|
|
12728
|
+
right: numericTerm(objectiveRef)
|
|
12729
|
+
}
|
|
12730
|
+
});
|
|
12731
|
+
objectiveRef = objVar;
|
|
12732
|
+
}
|
|
12733
|
+
const objectiveSortName = problem.objective.direction === "maximize" ? "maximize_objective" : "minimize_objective";
|
|
12734
|
+
allAntecedents.push({
|
|
12735
|
+
sort_name: objectiveSortName,
|
|
12736
|
+
features: {
|
|
12737
|
+
expression: objectiveRef,
|
|
12738
|
+
name: "objective"
|
|
12739
|
+
}
|
|
12740
|
+
});
|
|
12741
|
+
const variableRefList = variableNames.map(
|
|
12742
|
+
(name) => varRefs[name]
|
|
12743
|
+
);
|
|
12744
|
+
allAntecedents.push({
|
|
12745
|
+
sort_name: "real_labeling_constraint",
|
|
12746
|
+
features: {
|
|
12747
|
+
variables: variableRefList
|
|
12748
|
+
}
|
|
12749
|
+
});
|
|
12750
|
+
const solutionFeatures = {};
|
|
12751
|
+
for (const name of variableNames) {
|
|
12752
|
+
solutionFeatures[name] = varRefs[name];
|
|
12753
|
+
}
|
|
12754
|
+
solutionFeatures["_objective"] = objectiveRef;
|
|
12755
|
+
const solutionTerm = {
|
|
12756
|
+
sort_name: solutionSortName,
|
|
12757
|
+
features: solutionFeatures
|
|
12758
|
+
};
|
|
12759
|
+
return { solutionTerm, antecedents: allAntecedents };
|
|
12760
|
+
}
|
|
12761
|
+
function discoverVariables(problem) {
|
|
12762
|
+
const vars = /* @__PURE__ */ new Set();
|
|
12763
|
+
for (const name of Object.keys(problem.objective.coefficients)) {
|
|
12764
|
+
vars.add(name);
|
|
12765
|
+
}
|
|
12766
|
+
for (const constraint of problem.constraints) {
|
|
12767
|
+
for (const name of Object.keys(constraint.coefficients)) {
|
|
12768
|
+
vars.add(name);
|
|
12769
|
+
}
|
|
12770
|
+
}
|
|
12771
|
+
if (problem.bounds) {
|
|
12772
|
+
for (const name of Object.keys(problem.bounds)) {
|
|
12773
|
+
vars.add(name);
|
|
12774
|
+
}
|
|
12775
|
+
}
|
|
12776
|
+
return Array.from(vars).sort();
|
|
12777
|
+
}
|
|
12778
|
+
function compileLinearExpression(expression, varRefs, nextVar) {
|
|
12779
|
+
const antecedents = [];
|
|
12780
|
+
const termResults = [];
|
|
12781
|
+
for (const [varName, coefficient] of Object.entries(expression)) {
|
|
12782
|
+
if (coefficient === 0) continue;
|
|
12783
|
+
const varRef = varRefs[varName];
|
|
12784
|
+
if (!varRef) continue;
|
|
12785
|
+
if (coefficient === 1) {
|
|
12786
|
+
termResults.push(varRef);
|
|
12787
|
+
} else {
|
|
12788
|
+
const resultVar = { name: nextVar() };
|
|
12789
|
+
antecedents.push({
|
|
12790
|
+
sort_name: "real_times_constraint",
|
|
12791
|
+
features: {
|
|
12792
|
+
coefficient: numericTerm(coefficient),
|
|
12793
|
+
variable: varRef,
|
|
12794
|
+
result: resultVar
|
|
12795
|
+
}
|
|
12796
|
+
});
|
|
12797
|
+
termResults.push(resultVar);
|
|
12798
|
+
}
|
|
12799
|
+
}
|
|
12800
|
+
if (termResults.length === 0) {
|
|
12801
|
+
return { antecedents, resultRef: numericTerm(0) };
|
|
12802
|
+
}
|
|
12803
|
+
if (termResults.length === 1) {
|
|
12804
|
+
return { antecedents, resultRef: termResults[0] };
|
|
12805
|
+
}
|
|
12806
|
+
let current = termResults[0];
|
|
12807
|
+
for (let i = 1; i < termResults.length; i++) {
|
|
12808
|
+
const sumVar = { name: nextVar() };
|
|
12809
|
+
antecedents.push({
|
|
12810
|
+
sort_name: "real_plus_constraint",
|
|
12811
|
+
features: {
|
|
12812
|
+
left: current,
|
|
12813
|
+
right: termResults[i],
|
|
12814
|
+
result: sumVar
|
|
12815
|
+
}
|
|
12816
|
+
});
|
|
12817
|
+
current = sumVar;
|
|
12818
|
+
}
|
|
12819
|
+
return { antecedents, resultRef: current };
|
|
12820
|
+
}
|
|
12821
|
+
function constraintOpToSort(op) {
|
|
12822
|
+
switch (op) {
|
|
12823
|
+
case "<=":
|
|
12824
|
+
return "real_le_constraint";
|
|
12825
|
+
case ">=":
|
|
12826
|
+
return "real_ge_constraint";
|
|
12827
|
+
case "=":
|
|
12828
|
+
return "real_eq_constraint";
|
|
12829
|
+
}
|
|
12830
|
+
}
|
|
12831
|
+
|
|
12832
|
+
// src/resources/optimize.ts
|
|
12833
|
+
var OptimizeClient = class {
|
|
12834
|
+
/** @internal */
|
|
12835
|
+
inferenceApi;
|
|
12836
|
+
/** @internal */
|
|
12837
|
+
sortsApi;
|
|
12838
|
+
/** @internal */
|
|
12839
|
+
queryApi;
|
|
12840
|
+
/** @internal */
|
|
12841
|
+
termsApi;
|
|
12842
|
+
/** @internal */
|
|
12843
|
+
tenantId;
|
|
12844
|
+
/** @internal */
|
|
12845
|
+
constructor(inferenceApi, sortsApi, queryApi, termsApi, tenantId) {
|
|
12846
|
+
this.inferenceApi = inferenceApi;
|
|
12847
|
+
this.sortsApi = sortsApi;
|
|
12848
|
+
this.queryApi = queryApi;
|
|
12849
|
+
this.termsApi = termsApi;
|
|
12850
|
+
this.tenantId = tenantId;
|
|
12851
|
+
}
|
|
12852
|
+
/**
|
|
12853
|
+
* Solve a linear program.
|
|
12854
|
+
*
|
|
12855
|
+
* @param problem - The LP problem definition with objective, constraints, and bounds.
|
|
12856
|
+
* @param options - Optional solver configuration (timeout, cleanup, etc.).
|
|
12857
|
+
* @returns The optimization result with variable values and objective value.
|
|
12858
|
+
*
|
|
12859
|
+
* @throws {Error} If the problem has no variables.
|
|
12860
|
+
*
|
|
12861
|
+
* @remarks
|
|
12862
|
+
* Internally creates a temporary sort and inference rule, runs backward chaining
|
|
12863
|
+
* to trigger the CLP(Q) simplex solver, then cleans up temporary artifacts.
|
|
12864
|
+
*
|
|
12865
|
+
* The solver uses exact rational arithmetic (Rational64). Solutions are converted
|
|
12866
|
+
* to JavaScript numbers (f64 approximation) for the response.
|
|
12867
|
+
*
|
|
12868
|
+
* @example
|
|
12869
|
+
* ```typescript
|
|
12870
|
+
* import { LP } from '@kortexya/reasoninglayer';
|
|
12871
|
+
*
|
|
12872
|
+
* const result = await client.optimize.solve({
|
|
12873
|
+
* objective: LP.maximize({ x: 1, y: 2 }),
|
|
12874
|
+
* constraints: [
|
|
12875
|
+
* LP.constraint({ x: 1, y: 1 }, '<=', 10),
|
|
12876
|
+
* ],
|
|
12877
|
+
* bounds: LP.nonNegative('x', 'y'),
|
|
12878
|
+
* });
|
|
12879
|
+
*
|
|
12880
|
+
* if (result.status === 'optimal') {
|
|
12881
|
+
* console.log(result.variables); // { x: 0, y: 10 }
|
|
12882
|
+
* console.log(result.objectiveValue); // 20
|
|
12883
|
+
* }
|
|
12884
|
+
* ```
|
|
12885
|
+
*/
|
|
12886
|
+
async solve(problem, options) {
|
|
12887
|
+
const cleanup = options?.cleanup !== false;
|
|
12888
|
+
const timeoutMs = options?.timeoutMs ?? 3e4;
|
|
12889
|
+
const maxDepth = options?.maxDepth ?? 50;
|
|
12890
|
+
const suffix = generateShortId();
|
|
12891
|
+
const solutionSortName = `_lp_solution_${suffix}`;
|
|
12892
|
+
const compiled = compileLP(problem, solutionSortName);
|
|
12893
|
+
let sortId;
|
|
12894
|
+
let ruleTermId;
|
|
12895
|
+
try {
|
|
12896
|
+
const sortResponse = await this.sortsApi.bulkCreateSorts({
|
|
12897
|
+
sorts: [{ name: solutionSortName, parents: ["thing"] }]
|
|
12898
|
+
});
|
|
12899
|
+
sortId = sortResponse.data.sort_ids[solutionSortName];
|
|
12900
|
+
const ruleResponse = await this.inferenceApi.addRule({
|
|
12901
|
+
term: compiled.solutionTerm,
|
|
12902
|
+
antecedents: compiled.antecedents,
|
|
12903
|
+
certainty: 1
|
|
12904
|
+
});
|
|
12905
|
+
ruleTermId = ruleResponse.data.term.term_id;
|
|
12906
|
+
const bcResponse = await this.inferenceApi.backwardChain({
|
|
12907
|
+
goal: { sort_name: solutionSortName },
|
|
12908
|
+
max_solutions: 1,
|
|
12909
|
+
max_depth: maxDepth,
|
|
12910
|
+
timeout_ms: timeoutMs
|
|
12911
|
+
});
|
|
12912
|
+
const queryTimeMs = bcResponse.data.query_time_ms;
|
|
12913
|
+
if (bcResponse.data.solutions.length === 0) {
|
|
12914
|
+
return { status: "infeasible", solveTimeMs: queryTimeMs };
|
|
12915
|
+
}
|
|
12916
|
+
const solution = bcResponse.data.solutions[0];
|
|
12917
|
+
const variables = {};
|
|
12918
|
+
for (const binding of solution.substitution.bindings) {
|
|
12919
|
+
const varName = binding.variable_name;
|
|
12920
|
+
if (!varName) continue;
|
|
12921
|
+
const value = parseFloat(binding.bound_to_display);
|
|
12922
|
+
if (isNaN(value)) continue;
|
|
12923
|
+
if (varName.startsWith("?") && !varName.startsWith("?_lp_")) {
|
|
12924
|
+
variables[varName.slice(1)] = value;
|
|
12925
|
+
}
|
|
12926
|
+
}
|
|
12927
|
+
let objectiveValue = 0;
|
|
12928
|
+
for (const [varName, coeff] of Object.entries(problem.objective.coefficients)) {
|
|
12929
|
+
objectiveValue += coeff * (variables[varName] ?? 0);
|
|
12930
|
+
}
|
|
12931
|
+
return {
|
|
12932
|
+
status: "optimal",
|
|
12933
|
+
variables,
|
|
12934
|
+
objectiveValue,
|
|
12935
|
+
solveTimeMs: queryTimeMs
|
|
12936
|
+
};
|
|
12937
|
+
} finally {
|
|
12938
|
+
if (cleanup) {
|
|
12939
|
+
await this.cleanupArtifacts(ruleTermId, sortId);
|
|
12940
|
+
}
|
|
12941
|
+
}
|
|
12942
|
+
}
|
|
12943
|
+
/**
|
|
12944
|
+
* Solve an LP problem with variables and coefficients discovered from the knowledge base.
|
|
12945
|
+
*
|
|
12946
|
+
* @param config - Configuration specifying which sorts/features to query and how to
|
|
12947
|
+
* map them to LP variables, objective, and constraints.
|
|
12948
|
+
* @param options - Optional solver configuration.
|
|
12949
|
+
* @returns The optimization result plus the auto-generated LP and discovered terms.
|
|
12950
|
+
*
|
|
12951
|
+
* @throws {Error} If the specified sort is not found.
|
|
12952
|
+
* @throws {Error} If no terms are found for the variable sort.
|
|
12953
|
+
* @throws {Error} If a required feature is missing or non-numeric on a term.
|
|
12954
|
+
*
|
|
12955
|
+
* @remarks
|
|
12956
|
+
* This method queries existing knowledge base data to build the LP automatically:
|
|
12957
|
+
* 1. Resolves the variable sort name to its sort ID
|
|
12958
|
+
* 2. Queries all terms of that sort
|
|
12959
|
+
* 3. Extracts feature values to compute objective coefficients and constraint coefficients
|
|
12960
|
+
* 4. Builds and solves the LP
|
|
12961
|
+
*
|
|
12962
|
+
* @example
|
|
12963
|
+
* ```typescript
|
|
12964
|
+
* // Given products in the KB with features: name, profit, wood_cost, labor_cost
|
|
12965
|
+
* const result = await client.optimize.fromKnowledgeBase({
|
|
12966
|
+
* variables: { sort: 'product', nameFeature: 'name' },
|
|
12967
|
+
* objective: { direction: 'maximize', coefficientFeature: 'profit' },
|
|
12968
|
+
* resourceConstraints: [
|
|
12969
|
+
* { costFeature: 'wood_cost', capacity: 12, label: 'wood' },
|
|
12970
|
+
* { costFeature: 'labor_cost', capacity: 8, label: 'labor' },
|
|
12971
|
+
* ],
|
|
12972
|
+
* nonNegative: true,
|
|
12973
|
+
* });
|
|
12974
|
+
*
|
|
12975
|
+
* if (result.result.status === 'optimal') {
|
|
12976
|
+
* console.log(result.result.variables); // { chair: 2.4, table: 3.2 }
|
|
12977
|
+
* }
|
|
12978
|
+
* console.log(result.generatedProblem); // The auto-generated LP for inspection
|
|
12979
|
+
* ```
|
|
12980
|
+
*/
|
|
12981
|
+
async fromKnowledgeBase(config, options) {
|
|
12982
|
+
const sortId = await this.resolveSortName(config.variables.sort);
|
|
12983
|
+
const queryResponse = await this.queryApi.findBySort({
|
|
12984
|
+
sort_id: sortId
|
|
12985
|
+
});
|
|
12986
|
+
const terms = queryResponse.data.terms;
|
|
12987
|
+
if (terms.length === 0) {
|
|
12988
|
+
throw new Error(
|
|
12989
|
+
`No terms found for sort '${config.variables.sort}'. Create terms with features [${config.objective.coefficientFeature}, ${config.resourceConstraints.map((r) => r.costFeature).join(", ")}] first.`
|
|
12990
|
+
);
|
|
12991
|
+
}
|
|
12992
|
+
const variableNames = [];
|
|
12993
|
+
const objectiveCoeffs = {};
|
|
12994
|
+
const constraintCoeffsMap = {};
|
|
12995
|
+
for (const rc of config.resourceConstraints) {
|
|
12996
|
+
constraintCoeffsMap[rc.costFeature] = {};
|
|
12997
|
+
}
|
|
12998
|
+
for (const term of terms) {
|
|
12999
|
+
const name = extractStringValue(term.features, config.variables.nameFeature);
|
|
13000
|
+
if (!name) {
|
|
13001
|
+
throw new Error(
|
|
13002
|
+
`Term ${term.id} of sort '${config.variables.sort}' is missing the name feature '${config.variables.nameFeature}' or it is not a string.`
|
|
13003
|
+
);
|
|
13004
|
+
}
|
|
13005
|
+
variableNames.push(name);
|
|
13006
|
+
const objCoeff = extractNumericValue(term.features, config.objective.coefficientFeature);
|
|
13007
|
+
if (objCoeff === null) {
|
|
13008
|
+
throw new Error(
|
|
13009
|
+
`Term '${name}' (${term.id}) is missing numeric feature '${config.objective.coefficientFeature}' for the objective.`
|
|
13010
|
+
);
|
|
13011
|
+
}
|
|
13012
|
+
objectiveCoeffs[name] = objCoeff;
|
|
13013
|
+
for (const rc of config.resourceConstraints) {
|
|
13014
|
+
const costCoeff = extractNumericValue(term.features, rc.costFeature);
|
|
13015
|
+
if (costCoeff === null) {
|
|
13016
|
+
throw new Error(
|
|
13017
|
+
`Term '${name}' (${term.id}) is missing numeric feature '${rc.costFeature}' for the resource constraint.`
|
|
13018
|
+
);
|
|
13019
|
+
}
|
|
13020
|
+
constraintCoeffsMap[rc.costFeature][name] = costCoeff;
|
|
13021
|
+
}
|
|
13022
|
+
}
|
|
13023
|
+
const constraints = config.resourceConstraints.map((rc) => ({
|
|
13024
|
+
coefficients: constraintCoeffsMap[rc.costFeature],
|
|
13025
|
+
op: rc.op ?? "<=",
|
|
13026
|
+
rhs: rc.capacity,
|
|
13027
|
+
...rc.label ? { label: rc.label } : {}
|
|
13028
|
+
}));
|
|
13029
|
+
if (config.additionalConstraints) {
|
|
13030
|
+
constraints.push(...config.additionalConstraints);
|
|
13031
|
+
}
|
|
13032
|
+
const generatedProblem = {
|
|
13033
|
+
objective: {
|
|
13034
|
+
direction: config.objective.direction,
|
|
13035
|
+
coefficients: objectiveCoeffs
|
|
13036
|
+
},
|
|
13037
|
+
constraints,
|
|
13038
|
+
bounds: config.nonNegative ? Object.fromEntries(variableNames.map((n) => [n, { min: 0 }])) : void 0
|
|
13039
|
+
};
|
|
13040
|
+
const result = await this.solve(generatedProblem, options);
|
|
13041
|
+
return {
|
|
13042
|
+
result,
|
|
13043
|
+
generatedProblem,
|
|
13044
|
+
discoveredTerms: terms
|
|
13045
|
+
};
|
|
13046
|
+
}
|
|
13047
|
+
/**
|
|
13048
|
+
* Resolve a sort name to its sort ID by listing the tenant's sorts.
|
|
13049
|
+
*
|
|
13050
|
+
* @internal
|
|
13051
|
+
*/
|
|
13052
|
+
async resolveSortName(sortName) {
|
|
13053
|
+
const response = await this.sortsApi.listSorts(this.tenantId);
|
|
13054
|
+
const sorts = response.data.sorts;
|
|
13055
|
+
const match = sorts.find((s) => s.name === sortName);
|
|
13056
|
+
if (!match) {
|
|
13057
|
+
const available = sorts.slice(0, 10).map((s) => s.name).join(", ");
|
|
13058
|
+
throw new Error(
|
|
13059
|
+
`Sort '${sortName}' not found. Available sorts: ${available}${sorts.length > 10 ? "..." : ""}`
|
|
13060
|
+
);
|
|
13061
|
+
}
|
|
13062
|
+
return match.id;
|
|
13063
|
+
}
|
|
13064
|
+
/**
|
|
13065
|
+
* Clean up temporary sort and rule artifacts (best-effort).
|
|
13066
|
+
*
|
|
13067
|
+
* @internal
|
|
13068
|
+
*/
|
|
13069
|
+
async cleanupArtifacts(ruleTermId, sortId) {
|
|
13070
|
+
const promises = [];
|
|
13071
|
+
if (ruleTermId) {
|
|
13072
|
+
promises.push(
|
|
13073
|
+
this.termsApi.deleteTerm(ruleTermId).catch(() => {
|
|
13074
|
+
})
|
|
13075
|
+
);
|
|
13076
|
+
}
|
|
13077
|
+
if (sortId) {
|
|
13078
|
+
promises.push(
|
|
13079
|
+
this.sortsApi.deleteSort(sortId).catch(() => {
|
|
13080
|
+
})
|
|
13081
|
+
);
|
|
13082
|
+
}
|
|
13083
|
+
await Promise.all(promises);
|
|
13084
|
+
}
|
|
13085
|
+
};
|
|
13086
|
+
function generateShortId() {
|
|
13087
|
+
const timestamp = Date.now().toString(36);
|
|
13088
|
+
const random = Math.random().toString(36).slice(2, 8);
|
|
13089
|
+
return `${timestamp}_${random}`;
|
|
13090
|
+
}
|
|
13091
|
+
function extractStringValue(features, featureName) {
|
|
13092
|
+
const value = features[featureName];
|
|
13093
|
+
if (!value) return null;
|
|
13094
|
+
if (value.type === "String") return value.value;
|
|
13095
|
+
return null;
|
|
13096
|
+
}
|
|
13097
|
+
function extractNumericValue(features, featureName) {
|
|
13098
|
+
const value = features[featureName];
|
|
13099
|
+
if (!value) return null;
|
|
13100
|
+
if (value.type === "Integer") return value.value;
|
|
13101
|
+
if (value.type === "Real") return value.value;
|
|
13102
|
+
return null;
|
|
13103
|
+
}
|
|
13104
|
+
|
|
12551
13105
|
// src/client.ts
|
|
12552
13106
|
var ReasoningLayerClient = class {
|
|
12553
13107
|
/** Sort (type hierarchy) operations. */
|
|
@@ -12634,6 +13188,8 @@ var ReasoningLayerClient = class {
|
|
|
12634
13188
|
generation;
|
|
12635
13189
|
/** Ontology RAG operations (embedding search + feature unification). */
|
|
12636
13190
|
rag;
|
|
13191
|
+
/** Linear program optimization (CLP(Q) simplex solver via backward chaining). */
|
|
13192
|
+
optimize;
|
|
12637
13193
|
/**
|
|
12638
13194
|
* Create a new ReasoningLayerClient.
|
|
12639
13195
|
*
|
|
@@ -12736,6 +13292,7 @@ var ReasoningLayerClient = class {
|
|
|
12736
13292
|
this.ontology = new OntologyClient(generatedOntology);
|
|
12737
13293
|
this.generation = new GenerationClient(generatedGeneration);
|
|
12738
13294
|
this.rag = new RagClient(generatedRag);
|
|
13295
|
+
this.optimize = new OptimizeClient(generatedInference, generatedSorts, generatedQuery, generatedTerms, resolved.tenantId);
|
|
12739
13296
|
}
|
|
12740
13297
|
};
|
|
12741
13298
|
|
|
@@ -13610,6 +14167,7 @@ exports.ConstraintViolationError = ConstraintViolationError;
|
|
|
13610
14167
|
exports.FeatureInput = FeatureInput;
|
|
13611
14168
|
exports.FuzzyShape = FuzzyShape;
|
|
13612
14169
|
exports.InternalServerError = InternalServerError;
|
|
14170
|
+
exports.LP = LP;
|
|
13613
14171
|
exports.NetworkError = NetworkError;
|
|
13614
14172
|
exports.NotFoundError = NotFoundError;
|
|
13615
14173
|
exports.RateLimitError = RateLimitError;
|