@breadboard-ai/build
Advanced tools
Comparing version
# Changelog | ||
## 0.7.1 | ||
### Patch Changes | ||
- da43bb5: Allow wiring inputs directly to outputs | ||
- 5cf08f1: Add "wires" property to NodeDescriberContext which exposes a describe() function for getting the actual schema of a connected port if needed. | ||
- 9d93cf8: Fix bug relating to directly returning JSON Schema from describe function instead of wrapping with unsafeSchema. | ||
- 26e1099: Add describe function to Board component class | ||
- Updated dependencies [a925cf0] | ||
- Updated dependencies [5cf08f1] | ||
- Updated dependencies [ffbf163] | ||
- Updated dependencies [8928fb7] | ||
- Updated dependencies [d6706f2] | ||
- Updated dependencies [5447426] | ||
- Updated dependencies [7e1f01c] | ||
- @google-labs/breadboard@0.22.0 | ||
## 0.7.0 | ||
@@ -4,0 +21,0 @@ |
@@ -6,7 +6,7 @@ /** | ||
*/ | ||
import type { Convergence } from "./internal/board/converge.js"; | ||
import type { Input, InputWithDefault } from "./internal/board/input.js"; | ||
import type { Loopback } from "./internal/board/loopback.js"; | ||
import type { OutputPortReference } from "./internal/common/port.js"; | ||
import type { JsonSerializable } from "./internal/type-system/type.js"; | ||
import { type Convergence } from "./internal/board/converge.js"; | ||
import { type Input, type InputWithDefault } from "./internal/board/input.js"; | ||
import { type Loopback } from "./internal/board/loopback.js"; | ||
import { type OutputPortReference } from "./internal/common/port.js"; | ||
import type { BreadboardType, JsonSerializable } from "./internal/type-system/type.js"; | ||
export { board } from "./internal/board/board.js"; | ||
@@ -31,2 +31,3 @@ export { constant } from "./internal/board/constant.js"; | ||
export { toJSONSchema } from "./internal/type-system/type.js"; | ||
export { jsonSchemaToPortConfigMap as fromJSONSchema } from "./internal/define/json-schema.js"; | ||
export { unsafeType } from "./internal/type-system/unsafe.js"; | ||
@@ -45,1 +46,5 @@ /** | ||
export type Value<T extends JsonSerializable> = T | OutputPortReference<T> | Input<T> | InputWithDefault<T> | Loopback<T> | Convergence<T>; | ||
/** | ||
* Given a Breadboard {@link Value}, determine its JSON Schema type. | ||
*/ | ||
export declare function extractTypeFromValue(value: Value<JsonSerializable>): BreadboardType; |
@@ -6,2 +6,7 @@ /** | ||
*/ | ||
import { isConvergence } from "./internal/board/converge.js"; | ||
import { isSpecialInput, } from "./internal/board/input.js"; | ||
import { isLoopback } from "./internal/board/loopback.js"; | ||
import { isOutputPortReference, OutputPortGetter, } from "./internal/common/port.js"; | ||
import { anyOf } from "./internal/type-system/any-of.js"; | ||
export { board } from "./internal/board/board.js"; | ||
@@ -24,3 +29,34 @@ export { constant } from "./internal/board/constant.js"; | ||
export { toJSONSchema } from "./internal/type-system/type.js"; | ||
export { jsonSchemaToPortConfigMap as fromJSONSchema } from "./internal/define/json-schema.js"; | ||
export { unsafeType } from "./internal/type-system/unsafe.js"; | ||
/** | ||
* Given a Breadboard {@link Value}, determine its JSON Schema type. | ||
*/ | ||
export function extractTypeFromValue(value) { | ||
if (typeof value === "string") { | ||
return "string"; | ||
} | ||
if (typeof value === "number") { | ||
return "number"; | ||
} | ||
if (typeof value === "boolean") { | ||
return "boolean"; | ||
} | ||
if (value === null) { | ||
return "null"; | ||
} | ||
if (isOutputPortReference(value)) { | ||
return value[OutputPortGetter].type; | ||
} | ||
if (isSpecialInput(value)) { | ||
return value.type; | ||
} | ||
if (isLoopback(value)) { | ||
return value.type; | ||
} | ||
if (isConvergence(value)) { | ||
return anyOf(...value.ports.map((port) => extractTypeFromValue(port))); | ||
} | ||
return "unknown"; | ||
} | ||
//# sourceMappingURL=index.js.map |
@@ -6,7 +6,10 @@ /** | ||
*/ | ||
import type { NodeDescriberResult } from "@google-labs/breadboard"; | ||
import type { GraphMetadata } from "@google-labs/breadboard-schema/graph.js"; | ||
import type { JSONSchema4 } from "json-schema"; | ||
import { InputPort, OutputPort, type OutputPortReference, type ValuesOrOutputPorts } from "../common/port.js"; | ||
import type { JsonSerializable } from "../type-system/type.js"; | ||
import type { GenericSpecialInput, Input } from "./input.js"; | ||
import type { Output } from "./output.js"; | ||
import type { SerializableInputPort, SerializableOutputPortReference } from "../common/serializable.js"; | ||
import { type JsonSerializable } from "../type-system/type.js"; | ||
import { type GenericSpecialInput, type Input, type InputWithDefault } from "./input.js"; | ||
import { type Output } from "./output.js"; | ||
/** | ||
@@ -63,3 +66,3 @@ * Define a new Breadboard board. | ||
export type BoardOutputShape = BoardOutputPorts | Array<BoardOutputPortsWithUndefined>; | ||
export type BoardOutputPorts = Record<string, OutputPortReference<JsonSerializable> | Output<JsonSerializable>>; | ||
export type BoardOutputPorts = Record<string, OutputPortReference<JsonSerializable> | Output<JsonSerializable> | Input<JsonSerializable> | InputWithDefault<JsonSerializable>>; | ||
export type BoardOutputPortsWithUndefined = Record<string, OutputPortReference<JsonSerializable> | Output<JsonSerializable> | string | { | ||
@@ -85,2 +88,3 @@ title?: string; | ||
readonly metadata?: GraphMetadata; | ||
describe(): Promise<NodeDescriberResult>; | ||
}; | ||
@@ -99,2 +103,11 @@ export type GenericBoardDefinition = BoardDefinition<any, any>; | ||
export declare function isBoard(value: unknown): value is GenericBoardDefinition; | ||
export declare function describeInput(input: Input<JsonSerializable | undefined> | InputWithDefault<JsonSerializable | undefined> | SerializableInputPort): { | ||
schema: JSONSchema4; | ||
required: boolean; | ||
}; | ||
export declare function describeOutput(output: SerializableOutputPortReference | Output<JsonSerializable> | Input<JsonSerializable> | InputWithDefault<JsonSerializable>): { | ||
schema: JSONSchema4; | ||
required: boolean; | ||
}; | ||
export declare function isSerializableOutputPortReference(value: unknown): value is SerializableOutputPortReference; | ||
export {}; |
@@ -6,3 +6,7 @@ /** | ||
*/ | ||
import { InputPort, OutputPort, } from "../common/port.js"; | ||
import { InputPort, isOutputPortReference, OutputPort, OutputPortGetter, } from "../common/port.js"; | ||
import { toJSONSchema } from "../type-system/type.js"; | ||
import { isSpecialInput, } from "./input.js"; | ||
import { isOptional } from "./optional.js"; | ||
import { isSpecialOutput } from "./output.js"; | ||
// TODO(aomarks) Support primary ports in boards. | ||
@@ -46,2 +50,3 @@ // TODO(aomarks) Support adding descriptions to board ports. | ||
isBoard: true, | ||
describe: def.describe.bind(def), | ||
}); | ||
@@ -88,3 +93,42 @@ } | ||
} | ||
async describe() { | ||
const requiredInputs = []; | ||
const requiredOutputs = []; | ||
return { | ||
inputSchema: { | ||
type: "object", | ||
required: requiredInputs, | ||
additionalProperties: false, | ||
properties: Object.fromEntries(Object.entries(this.#inputs).map(([name, input]) => { | ||
const { schema, required } = describeInput(input); | ||
if (required) { | ||
requiredInputs.push(name); | ||
} | ||
return [name, schema]; | ||
})), | ||
}, | ||
outputSchema: { | ||
type: "object", | ||
required: requiredOutputs, | ||
additionalProperties: false, | ||
properties: Object.fromEntries(Object.entries(this.#outputs).map(([name, output]) => { | ||
const { schema, required } = describeOutput(output); | ||
if (required) { | ||
requiredOutputs.push(name); | ||
} | ||
return [name, schema]; | ||
})), | ||
}, | ||
}; | ||
} | ||
} | ||
function getSchema(value) { | ||
if ("type" in value) { | ||
return toJSONSchema(value.type); | ||
} | ||
if (OutputPortGetter in value) { | ||
return getSchema(value[OutputPortGetter]); | ||
} | ||
return getSchema(value.port); | ||
} | ||
class BoardInstance { | ||
@@ -104,2 +148,61 @@ inputs; | ||
} | ||
export function describeInput(input) { | ||
const schema = toJSONSchema(input.type); | ||
let isSpecialOptional = false; | ||
if (isSpecialInput(input)) { | ||
if (input.title !== undefined) { | ||
schema.title = input.title; | ||
} | ||
if (input.description !== undefined) { | ||
schema.description = input.description; | ||
} | ||
if (input.default !== undefined) { | ||
schema.default = | ||
typeof input.default === "string" | ||
? input.default | ||
: // TODO(aomarks) Why is default JSON stringified? The UI currently | ||
// requires it, but seems like it should be real JSON. | ||
JSON.stringify(input.default, null, 2); | ||
} | ||
if (input.examples !== undefined && input.examples.length > 0) { | ||
schema.examples = input.examples.map((example) => typeof example === "string" | ||
? example | ||
: // TODO(aomarks) Why is examples JSON stringified? The UI currently | ||
// requires it, but seems like it should be real JSON. | ||
JSON.stringify(example, null, 2)); | ||
} | ||
if ("optional" in input && input.optional) { | ||
isSpecialOptional = true; | ||
} | ||
} | ||
const required = schema.default === undefined && !isSpecialOptional; | ||
return { schema, required }; | ||
} | ||
export function describeOutput(output) { | ||
let port; | ||
if (isSpecialOutput(output)) { | ||
port = output.port; | ||
} | ||
else { | ||
port = output; | ||
} | ||
if (isSerializableOutputPortReference(port)) { | ||
port = port[OutputPortGetter]; | ||
} | ||
// Input<JsonSerializable> | InputWithDefault<JsonSerializable> | SerializableOutputPort | OutputPort<...> | ||
const schema = toJSONSchema(port.type); | ||
if (isSpecialOutput(output)) { | ||
if (output.title !== undefined) { | ||
schema.title = output.title; | ||
} | ||
if (output.description !== undefined) { | ||
schema.description = output.description; | ||
} | ||
} | ||
const required = !isOptional(port); | ||
return { schema, required }; | ||
} | ||
export function isSerializableOutputPortReference(value) { | ||
return (typeof value === "object" && value !== null && OutputPortGetter in value); | ||
} | ||
//# sourceMappingURL=board.js.map |
@@ -88,2 +88,3 @@ /** | ||
}; | ||
export declare function isSpecialInput(value: unknown): value is GenericSpecialInput; | ||
export {}; |
@@ -48,2 +48,7 @@ /** | ||
} | ||
export function isSpecialInput(value) { | ||
return (typeof value === "object" && | ||
value !== null && | ||
"__SpecialInputBrand" in value); | ||
} | ||
//# sourceMappingURL=input.js.map |
@@ -8,3 +8,4 @@ /** | ||
import type { JsonSerializable } from "../type-system/type.js"; | ||
export declare function output<T extends JsonSerializable>(port: OutputPortReference<T>, { id, title, description, }?: { | ||
import type { Input, InputWithDefault } from "./input.js"; | ||
export declare function output<T extends JsonSerializable>(port: OutputPortReference<T> | Input<T> | InputWithDefault<T>, { id, title, description, }?: { | ||
id?: string; | ||
@@ -19,3 +20,4 @@ title?: string; | ||
readonly description?: string; | ||
readonly port: OutputPortReference<T>; | ||
readonly port: OutputPortReference<T> | Input<T> | InputWithDefault<T>; | ||
} | ||
export declare function isSpecialOutput(value: unknown): value is Output<JsonSerializable>; |
@@ -15,2 +15,7 @@ /** | ||
} | ||
export function isSpecialOutput(value) { | ||
return (typeof value === "object" && | ||
value !== null && | ||
"__SpecialOutputBrand" in value); | ||
} | ||
//# sourceMappingURL=output.js.map |
@@ -7,8 +7,10 @@ /** | ||
import { DefaultValue, OutputPortGetter } from "../common/port.js"; | ||
import { toJSONSchema } from "../type-system/type.js"; | ||
import {} from "../type-system/type.js"; | ||
import { describeInput, describeOutput, isBoard, isSerializableOutputPortReference, } from "./board.js"; | ||
import { ConstantVersionOf, isConstant } from "./constant.js"; | ||
import { isConvergence } from "./converge.js"; | ||
import { isSpecialInput } from "./input.js"; | ||
import { isLoopback } from "./loopback.js"; | ||
import { OptionalVersionOf, isOptional } from "./optional.js"; | ||
import { isBoard } from "./board.js"; | ||
import { isSpecialOutput } from "./output.js"; | ||
/** | ||
@@ -37,2 +39,3 @@ * Serialize a Breadboard board to Breadboard Graph Language (BGL) so that it | ||
let i = 0; | ||
const magicInputResolutions = new Map(); | ||
for (const inputs of inputsArray) { | ||
@@ -69,30 +72,3 @@ const sortedBoardInputs = Object.entries(inputs).sort( | ||
unconnectedInputs.add(input); | ||
const schema = toJSONSchema(input.type); | ||
let isSpecialOptional = false; | ||
if (isSpecialInput(input)) { | ||
if (input.title !== undefined) { | ||
schema.title = input.title; | ||
} | ||
if (input.description !== undefined) { | ||
schema.description = input.description; | ||
} | ||
if (input.default !== undefined) { | ||
schema.default = | ||
typeof input.default === "string" | ||
? input.default | ||
: // TODO(aomarks) Why is default JSON stringified? The UI currently | ||
// requires it, but seems like it should be real JSON. | ||
JSON.stringify(input.default, null, 2); | ||
} | ||
if (input.examples !== undefined && input.examples.length > 0) { | ||
schema.examples = input.examples.map((example) => typeof example === "string" | ||
? example | ||
: // TODO(aomarks) Why is examples JSON stringified? The UI currently | ||
// requires it, but seems like it should be real JSON. | ||
JSON.stringify(example, null, 2)); | ||
} | ||
if ("optional" in input && input.optional) { | ||
isSpecialOptional = true; | ||
} | ||
} | ||
const { schema, required } = describeInput(input); | ||
let inputNode = inputNodes.get(inputNodeId); | ||
@@ -131,5 +107,11 @@ if (inputNode === undefined) { | ||
]); | ||
if (schema.default === undefined && !isSpecialOptional) { | ||
if (required) { | ||
inputNode.configuration.schema.required.push(mainInputName); | ||
} | ||
if (isSpecialInput(input)) { | ||
magicInputResolutions.set(input, { | ||
nodeId: inputNodeId, | ||
portName: mainInputName, | ||
}); | ||
} | ||
} | ||
@@ -159,8 +141,24 @@ } | ||
} | ||
const port = isSpecialOutput(output) | ||
? output.port[OutputPortGetter] | ||
: output[OutputPortGetter]; | ||
const outputNodeId = outputs.$id ?? | ||
(isSpecialOutput(output) ? output.id : undefined) ?? | ||
autoId(); | ||
let port; | ||
let outputNodeId; | ||
const portMetadata = {}; | ||
if (isSpecialOutput(output)) { | ||
port = output.port; | ||
outputNodeId = output.id; | ||
if (output.title) { | ||
portMetadata.title = output.title; | ||
} | ||
if (output.description) { | ||
portMetadata.description = output.description; | ||
} | ||
} | ||
else { | ||
port = output; | ||
} | ||
if (isSerializableOutputPortReference(port)) { | ||
port = port[OutputPortGetter]; | ||
} | ||
// TODO(aomarks) Remove cast. | ||
outputNodeId ??= outputs.$id; | ||
outputNodeId ??= autoId(); | ||
let outputNode = outputNodes.get(outputNodeId); | ||
@@ -180,25 +178,23 @@ if (outputNode === undefined) { | ||
}; | ||
const metadata = outputs.$metadata; | ||
if (metadata !== undefined) { | ||
outputNode.metadata = metadata; | ||
const nodeMetadata = outputs.$metadata; | ||
if (nodeMetadata !== undefined) { | ||
outputNode.metadata = nodeMetadata; | ||
} | ||
outputNodes.set(outputNodeId, outputNode); | ||
} | ||
const schema = toJSONSchema(port.type); | ||
if (isSpecialOutput(output)) { | ||
if (output.title !== undefined) { | ||
schema.title = output.title; | ||
} | ||
if (output.description !== undefined) { | ||
schema.description = output.description; | ||
} | ||
} | ||
const { schema, required } = describeOutput(output); | ||
outputNode.configuration.schema.properties[name] = schema; | ||
const isOpt = isSpecialOutput(output) | ||
? isOptional(output.port) | ||
: isOptional(output); | ||
if (!isOpt) { | ||
if (required) { | ||
outputNode.configuration.schema.required.push(name); | ||
} | ||
addEdge(visitNodeAndReturnItsId(port.node), port.name, outputNodeId, name, isConstant(output), isOpt); | ||
if (isSpecialInput(port)) { | ||
unconnectedInputs.delete(port); | ||
const resolution = magicInputResolutions.get(port); | ||
if (resolution !== undefined) { | ||
addEdge(resolution.nodeId, resolution.portName, outputNodeId, name, isConstant(output), !required); | ||
} | ||
} | ||
else { | ||
addEdge(visitNodeAndReturnItsId(port.node), port.name, outputNodeId, name, isConstant(output), !required); | ||
} | ||
} | ||
@@ -313,3 +309,3 @@ } | ||
} | ||
else if (isOutputPortReference(value)) { | ||
else if (isSerializableOutputPortReference(value)) { | ||
const wiredOutputPort = value[OutputPortGetter]; | ||
@@ -380,15 +376,2 @@ addEdge(visitNodeAndReturnItsId(wiredOutputPort.node), wiredOutputPort.name, thisNodeId, portName, wasConstant, wasOptional); | ||
} | ||
function isSpecialInput(value) { | ||
return (typeof value === "object" && | ||
value !== null && | ||
"__SpecialInputBrand" in value); | ||
} | ||
function isSpecialOutput(value) { | ||
return (typeof value === "object" && | ||
value !== null && | ||
"__SpecialOutputBrand" in value); | ||
} | ||
function isOutputPortReference(value) { | ||
return (typeof value === "object" && value !== null && OutputPortGetter in value); | ||
} | ||
function sortKeys(obj, fieldOrder) { | ||
@@ -395,0 +378,0 @@ return Object.fromEntries(Object.entries(obj).sort(([nameA], [nameB]) => { |
@@ -17,4 +17,4 @@ /** | ||
inputsForSerialization: any; | ||
outputs: Record<string, SerializableOutputPortReference | Output<JsonSerializable>>; | ||
outputsForSerialization: Record<string, SerializableOutputPortReference | Output<JsonSerializable>> | Array<Record<string, SerializableOutputPortReference | Output<JsonSerializable>>>; | ||
outputs: Record<string, SerializableOutputPortReference | Output<JsonSerializable> | Input<JsonSerializable> | InputWithDefault<JsonSerializable>>; | ||
outputsForSerialization: Record<string, SerializableOutputPortReference | Output<JsonSerializable> | Input<JsonSerializable> | InputWithDefault<JsonSerializable>> | Array<Record<string, SerializableOutputPortReference | Output<JsonSerializable> | Input<JsonSerializable> | InputWithDefault<JsonSerializable>>>; | ||
title?: string; | ||
@@ -21,0 +21,0 @@ description?: string; |
@@ -109,3 +109,6 @@ /** | ||
user = await this.#describe(staticValues, dynamicValues, { | ||
...(context ?? { outerGraph: { nodes: [], edges: [] } }), | ||
...(context ?? { | ||
outerGraph: { nodes: [], edges: [] }, | ||
wires: { incoming: {}, outgoing: {} }, | ||
}), | ||
inputSchema: jsonSchemaToPortConfigMap(inboundEdges ?? {}), | ||
@@ -112,0 +115,0 @@ outputSchema: jsonSchemaToPortConfigMap(outboundEdges ?? {}), |
{ | ||
"name": "@breadboard-ai/build", | ||
"version": "0.7.0", | ||
"version": "0.7.1", | ||
"description": "JavaScript library for building boards and defining new node types for the Breadboard AI prototyping library", | ||
@@ -118,3 +118,3 @@ "license": "Apache-2.0", | ||
"dependencies": { | ||
"@google-labs/breadboard": "^0.21.0", | ||
"@google-labs/breadboard": "^0.22.0", | ||
"@types/json-schema": "^7.0.15" | ||
@@ -125,5 +125,5 @@ }, | ||
"eslint": "^8.57.0", | ||
"typescript": "^5.5.2", | ||
"wireit": "^0.14.4" | ||
"typescript": "^5.5.3", | ||
"wireit": "^0.14.5" | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
# @breadboard-ai/build | ||
# Breadboard Build | ||
@@ -3,0 +3,0 @@ [](https://www.npmjs.com/package/@breadboard-ai/build) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
328113
5.43%3144
5.26%+ Added
+ Added
- Removed