Make templating work more flexibly (#44)

* Restructure directory
* Config loading
* Variable override points in JSONNET templates, separate pulsar-manager template
* Bump version
* Tidy chunking
* Simplified prompt overrides
* Update config loader
* Fix recursive chunker template
This commit is contained in:
cybermaggedon 2024-08-30 17:47:35 +01:00 committed by GitHub
parent f10c2822f4
commit f7a30006ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
84 changed files with 1140 additions and 1164 deletions

View file

@ -1,8 +1,14 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local url = import "url.jsonnet";
local prompts = import "../prompts/mixtral.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local prompts = import "prompts/mixtral.jsonnet";
{
"azure-token":: "${AZURE_TOKEN}",
"azure-endpoint":: "${AZURE_ENDPOINT}",
"azure-max-output":: 4096,
"azure-temperature":: 0.0,
services +: {
"text-completion": base + {
@ -12,13 +18,13 @@ local prompts = import "../prompts/mixtral.jsonnet";
"-p",
url.pulsar,
"-k",
"${AZURE_TOKEN}",
$["azure-token"],
"-e",
"${AZURE_ENDPOINT}",
$["azure-endpoint"],
"-x",
"4096",
std.toString($["azure-max-output"]),
"-t",
"0.0",
std.toString($["azure-temperature"]),
],
deploy: {
resources: {
@ -41,14 +47,14 @@ local prompts = import "../prompts/mixtral.jsonnet";
"-p",
url.pulsar,
"-k",
"${AZURE_TOKEN}",
$["azure-token"],
"-e",
"${AZURE_ENDPOINT}",
$["azure-endpoint"],
"-i",
"-x",
"4096",
std.toString($["azure-max-output"]),
"-t",
"0.0",
std.toString($["azure-temperature"]),
"non-persistent://tg/request/text-completion-rag",
"-o",
"non-persistent://tg/response/text-completion-rag-response",
@ -70,4 +76,3 @@ local prompts = import "../prompts/mixtral.jsonnet";
},
} + prompts

View file

@ -1,3 +0,0 @@
{
restart: "on-failure:100",
}

View file

@ -1,34 +1,17 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local url = import "url.jsonnet";
local prompts = import "../prompts/mixtral.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local prompts = import "prompts/mixtral.jsonnet";
local chunker = import "chunker-recursive.jsonnet";
{
services +: {
"aws-id-key":: "${AWS_ID_KEY}",
"aws-secret-key":: "${AWS_SECRET_KEY}",
"aws-region":: "us-west-2",
"bedrock-max-output-tokens":: 4096,
"bedrock-temperature":: 0.0,
"bedrock-model":: "mistral.mixtral-8x7b-instruct-v0:1",
chunker: base + {
image: images.trustgraph,
command: [
"chunker-recursive",
"-p",
url.pulsar,
"--chunk-size",
"2000",
"--chunk-overlap",
"100",
],
deploy: {
resources: {
limits: {
cpus: '0.5',
memory: '128M'
},
reservations: {
cpus: '0.1',
memory: '128M'
}
}
},
},
services +: {
"text-completion": base + {
image: images.trustgraph,
@ -37,17 +20,17 @@ local prompts = import "../prompts/mixtral.jsonnet";
"-p",
url.pulsar,
"-z",
"${AWS_ID_KEY}",
$["aws-id-key"],
"-k",
"${AWS_SECRET_KEY}",
$["aws-secret-key"],
"-r",
"us-west-2",
$["aws-region"],
"-x",
"4096",
std.toString($["bedrock-max-output-tokens"]),
"-t",
"0.0",
std.toString($["bedrock-temperature"]),
"-m",
"mistral.mixtral-8x7b-instruct-v0:1",
$["bedrock-model"],
],
deploy: {
resources: {
@ -69,20 +52,18 @@ local prompts = import "../prompts/mixtral.jsonnet";
"text-completion-bedrock",
"-p",
url.pulsar,
// "-m",
// "mistral.mistral-large-2407-v1:0",
"-z",
"${AWS_ID_KEY}",
$["aws-id-key"],
"-k",
"${AWS_SECRET_KEY}",
$["aws-secret-key"],
"-r",
"us-west-2",
$["aws-region"],
"-x",
"4096",
std.toString($["bedrock-max-output-tokens"]),
"-t",
"0.0",
std.toString($["bedrock-temperature"]),
"-m",
"mistral.mixtral-8x7b-instruct-v0:1",
$["bedrock-model"],
"-i",
"non-persistent://tg/request/text-completion-rag",
"-o",
@ -103,6 +84,5 @@ local prompts = import "../prompts/mixtral.jsonnet";
},
},
} + prompts
} + prompts + chunker

View file

@ -1,6 +1,6 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local url = import "url.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local cassandra_hosts = "cassandra";
local cassandra = import "stores/cassandra.jsonnet";

View file

@ -0,0 +1,39 @@
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local prompts = import "prompts/mixtral.jsonnet";
{
"chunk-size":: 2000,
"chunk-overlap":: 100,
services +: {
chunker: base + {
image: images.trustgraph,
command: [
"chunker-recursive",
"-p",
url.pulsar,
"--chunk-size",
std.toString($["chunk-size"]),
"--chunk-overlap",
std.toString($["chunk-overlap"]),
],
deploy: {
resources: {
limits: {
cpus: '0.5',
memory: '128M'
},
reservations: {
cpus: '0.1',
memory: '128M'
}
}
},
},
},
} + prompts

View file

@ -1,8 +1,13 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local url = import "url.jsonnet";
local prompts = import "../prompts/mixtral.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local prompts = import "prompts/mixtral.jsonnet";
{
"claude-key":: "${CLAUDE_KEY}",
"claude-max-output-tokens":: 4096,
"claude-temperature":: 0.0,
services +: {
"text-completion": base + {
@ -12,11 +17,11 @@ local prompts = import "../prompts/mixtral.jsonnet";
"-p",
url.pulsar,
"-k",
"${CLAUDE_KEY}",
$["claude-key"],
"-x",
"4096",
std.toString($["claude-max-output-tokens"]),
"-t",
"0.0",
std.toString($["claude-temperature"]),
],
deploy: {
resources: {
@ -39,11 +44,11 @@ local prompts = import "../prompts/mixtral.jsonnet";
"-p",
url.pulsar,
"-k",
"${CLAUDE_KEY}",
$["claude-key"],
"-x",
"4096",
std.toString($["claude-max-output-tokens"]),
"-t",
"0.0",
std.toString($["claude-temperature"]),
"-i",
"non-persistent://tg/request/text-completion-rag",
"-o",

View file

@ -1,34 +1,17 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local url = import "url.jsonnet";
local prompts = import "../prompts/mixtral.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local prompts = import "prompts/cohere.jsonnet";
{
services +: {
chunker: base + {
image: images.trustgraph,
command: [
"${CHUNKER:-chunker-token}",
"-p",
url.pulsar,
"--chunk-size",
"150",
"--chunk-overlap",
"10",
],
deploy: {
resources: {
limits: {
cpus: '0.5',
memory: '128M'
},
reservations: {
cpus: '0.1',
memory: '128M'
}
}
},
},
// Override chunking
"chunk-size":: 150,
"chunk-overlap":: 10,
"cohere-key":: "${COHERE_KEY}",
"cohere-temperature":: 0.0,
services +: {
"text-completion": base + {
image: images.trustgraph,
@ -37,9 +20,9 @@ local prompts = import "../prompts/mixtral.jsonnet";
"-p",
url.pulsar,
"-k",
"${COHERE_KEY}",
$["cohere-key"],
"-t",
"0.0",
$["cohere-temperature"],
],
deploy: {
resources: {
@ -62,9 +45,9 @@ local prompts = import "../prompts/mixtral.jsonnet";
"-p",
url.pulsar,
"-k",
"${COHERE_KEY}",
$["cohere-key"],
"-t",
"0.0",
$["cohere-temperature"],
"-i",
"non-persistent://tg/request/text-completion-rag",
"-o",

View file

@ -1,5 +1,5 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
{
volumes +: {
"prometheus-data": {},

View file

@ -1,14 +0,0 @@
local version = import "version.jsonnet";
{
cassandra: "docker.io/cassandra:4.1.6",
neo4j: "docker.io/neo4j:5.22.0-community-bullseye",
pulsar: "docker.io/apachepulsar/pulsar:3.3.1",
pulsar_manager: "docker.io/apachepulsar/pulsar-manager:v0.4.0",
etcd: "quay.io/coreos/etcd:v3.5.15",
minio: "docker.io/minio/minio:RELEASE.2024-08-17T01-24-54Z",
milvus: "docker.io/milvusdb/milvus:v2.4.9",
prometheus: "docker.io/prom/prometheus:v2.53.2",
grafana: "docker.io/grafana/grafana:11.1.4",
trustgraph: "docker.io/trustgraph/trustgraph-flow:" + version,
qdrant: "docker.io/qdrant/qdrant:v1.11.1"
}

View file

@ -1,6 +1,6 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local url = import "url.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local milvus = import "stores/milvus.jsonnet";
milvus + {

View file

@ -1,6 +1,6 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local url = import "url.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local neo4j = import "stores/neo4j.jsonnet";
neo4j + {

View file

@ -0,0 +1,2 @@
{
}

View file

@ -1,7 +1,7 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local url = import "url.jsonnet";
local prompts = import "../prompts/slm.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local prompts = import "prompts/slm.jsonnet";
{
services +: {

View file

@ -1,8 +1,13 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local url = import "url.jsonnet";
local prompts = import "../prompts/openai.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local prompts = import "prompts/openai.jsonnet";
{
"openai-key":: "${OPENAI_KEY}",
"openai-max-output-tokens":: 4096,
"openai-temperature":: 0.0,
services +: {
"text-completion": base + {
@ -12,11 +17,11 @@ local prompts = import "../prompts/openai.jsonnet";
"-p",
url.pulsar,
"-k",
"${OPENAI_KEY}",
$["openai-key"],
"-x",
"4096",
std.toString($["openai-max-output-tokens"]),
"-t",
"0.0",
std.toString($["openai-temperature"]),
],
deploy: {
resources: {
@ -39,11 +44,11 @@ local prompts = import "../prompts/openai.jsonnet";
"-p",
url.pulsar,
"-k",
"${OPENAI_KEY}",
$["openai-key"],
"-x",
"4096",
std.toString($["openai-max-output-tokens"]),
"-t",
"0.0",
std.toString($["openai-temperature"]),
"-i",
"non-persistent://tg/request/text-completion-rag",
"-o",

View file

@ -0,0 +1,67 @@
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
{
services +: {
"prompt": base + {
image: images.trustgraph,
command: [
"prompt-generic",
"-p",
url.pulsar,
"--text-completion-request-queue",
"non-persistent://tg/request/text-completion",
"--text-completion-response-queue",
"non-persistent://tg/response/text-completion-response",
],
deploy: {
resources: {
limits: {
cpus: '0.5',
memory: '128M'
},
reservations: {
cpus: '0.1',
memory: '128M'
}
}
},
},
"prompt-rag": base + {
image: images.trustgraph,
command: [
"prompt-generic",
"-p",
url.pulsar,
"-i",
"non-persistent://tg/request/prompt-rag",
"-o",
"non-persistent://tg/response/prompt-rag-response",
"--text-completion-request-queue",
"non-persistent://tg/request/text-completion-rag",
"--text-completion-response-queue",
"non-persistent://tg/response/text-completion-rag-response",
],
deploy: {
resources: {
limits: {
cpus: '0.5',
memory: '128M'
},
reservations: {
cpus: '0.1',
memory: '128M'
}
}
},
},
}
}

View file

@ -0,0 +1,89 @@
// For VertexAI Gemini
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local default_prompts = import "prompts/default-prompts.jsonnet";
default_prompts + {
services +: {
"prompt": base + {
image: images.trustgraph,
command: [
"prompt-template",
"-p",
url.pulsar,
"--text-completion-request-queue",
"non-persistent://tg/request/text-completion",
"--text-completion-response-queue",
"non-persistent://tg/response/text-completion-response",
"--definition-template",
$["prompt-definition-template"],
"--relationship-template",
$["prompt-relationship-template"],
"--knowledge-query-template",
$["prompt-knowledge-query-template"],
"--document-query-template",
$["prompt-document-query-template"],
"--rows-template",
$["prompt-rows-template"],
],
deploy: {
resources: {
limits: {
cpus: '0.5',
memory: '128M'
},
reservations: {
cpus: '0.1',
memory: '128M'
}
}
},
},
"prompt-rag": base + {
image: images.trustgraph,
command: [
"prompt-template",
"-p",
url.pulsar,
"-i",
"non-persistent://tg/request/prompt-rag",
"-o",
"non-persistent://tg/response/prompt-rag-response",
"--text-completion-request-queue",
"non-persistent://tg/request/text-completion-rag",
"--text-completion-response-queue",
"non-persistent://tg/response/text-completion-rag-response",
"--definition-template",
$["prompt-definition-template"],
"--relationship-template",
$["prompt-relationship-template"],
"--knowledge-query-template",
$["prompt-knowledge-query-template"],
"--document-query-template",
$["prompt-document-query-template"],
"--rows-template",
$["prompt-rows-template"],
],
deploy: {
resources: {
limits: {
cpus: '0.5',
memory: '128M'
},
reservations: {
cpus: '0.1',
memory: '128M'
}
}
},
},
},
}

View file

@ -0,0 +1,33 @@
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
{
// FIXME: Should persist *something*
volumes +: {
},
services +: {
"pulsar-manager": base + {
image: images.pulsar_manager,
ports: [
"9527:9527",
"7750:7750",
],
environment: {
SPRING_CONFIGURATION_FILE: "/pulsar-manager/pulsar-manager/application.properties",
},
deploy: {
resources: {
limits: {
cpus: '0.5',
memory: '1.4G'
},
reservations: {
cpus: '0.1',
memory: '1.4G'
}
}
},
},
}
}

View file

@ -1,5 +1,5 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
{
volumes +: {
"pulsar-conf": {},
@ -58,27 +58,5 @@ local images = import "images.jsonnet";
}
},
},
"pulsar-manager": base + {
image: images.pulsar_manager,
ports: [
"9527:9527",
"7750:7750",
],
environment: {
SPRING_CONFIGURATION_FILE: "/pulsar-manager/pulsar-manager/application.properties",
},
deploy: {
resources: {
limits: {
cpus: '0.5',
memory: '1.4G'
},
reservations: {
cpus: '0.1',
memory: '1.4G'
}
}
},
},
}
}

View file

@ -1,6 +1,6 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local url = import "url.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local qdrant = import "stores/qdrant.jsonnet";
qdrant + {

View file

@ -1,35 +0,0 @@
local base = import "../base.jsonnet";
local images = import "../images.jsonnet";
{
volumes +: {
cassandra: {},
},
services +: {
cassandra: base + {
image: images.cassandra,
ports: [
"9042:9042"
],
environment: {
JVM_OPTS: "-Xms256M -Xmx256M",
},
volumes: [
"cassandra:/var/lib/cassandra"
],
deploy: {
resources: {
limits: {
cpus: '1.0',
memory: '800M'
},
reservations: {
cpus: '0.5',
memory: '800M'
}
}
},
},
},
}

View file

@ -1,115 +0,0 @@
local base = import "../base.jsonnet";
local images = import "../images.jsonnet";
{
volumes +: {
etcd: {},
"minio-data": {},
milvus: {},
},
services +: {
etcd: base + {
image: images.etcd,
command: [
"etcd",
"-advertise-client-urls=http://127.0.0.1:2379",
"-listen-client-urls",
"http://0.0.0.0:2379",
"--data-dir",
"/etcd",
],
environment: {
ETCD_AUTO_COMPACTION_MODE: "revision",
ETCD_AUTO_COMPACTION_RETENTION: "1000",
ETCD_QUOTA_BACKEND_BYTES: "4294967296",
ETCD_SNAPSHOT_COUNT: "50000"
},
ports: [
"2379:2379",
],
volumes: [
"etcd:/etcd"
],
deploy: {
resources: {
limits: {
cpus: '1.0',
memory: '128M'
},
reservations: {
cpus: '0.25',
memory: '128M'
}
},
},
},
minio: base + {
image: images.minio,
command: [
"minio",
"server",
"/minio_data",
"--console-address",
":9001",
],
environment: {
MINIO_ROOT_USER: "minioadmin",
MINIO_ROOT_PASSWORD: "minioadmin",
},
ports: [
"9001:9001",
],
volumes: [
"minio-data:/minio_data",
],
deploy: {
resources: {
limits: {
cpus: '0.5',
memory: '128M'
},
reservations: {
cpus: '0.25',
memory: '128M'
}
}
},
},
milvus: base + {
image: images.milvus,
command: [
"milvus", "run", "standalone"
],
environment: {
ETCD_ENDPOINTS: "etcd:2379",
MINIO_ADDRESS: "minio:9000",
},
ports: [
"9091:9091",
"19530:19530",
],
volumes: [
"milvus:/var/lib/milvus"
],
deploy: {
resources: {
limits: {
cpus: '1.0',
memory: '256M'
},
reservations: {
cpus: '0.5',
memory: '256M'
}
}
},
},
},
}

View file

@ -1,42 +0,0 @@
local base = import "../base.jsonnet";
local images = import "../images.jsonnet";
{
volumes +: {
neo4j: {},
},
services +: {
neo4j: base + {
image: images.neo4j,
ports: [
"7474:7474",
"7687:7687",
],
environment: {
NEO4J_AUTH: "neo4j/password",
// NEO4J_server_bolt_listen__address: "0.0.0.0:7687",
// NEO4J_server_default__listen__address: "0.0.0.0",
// NEO4J_server_http_listen__address: "0.0.0.0:7474",
},
volumes: [
"neo4j:/data"
],
deploy: {
resources: {
limits: {
cpus: '1.0',
memory: '768M'
},
reservations: {
cpus: '0.5',
memory: '768M'
}
}
},
},
},
}

View file

@ -1,37 +0,0 @@
local base = import "../base.jsonnet";
local images = import "../images.jsonnet";
{
volumes +: {
qdrant: {},
},
services +: {
qdrant: base + {
image: images.qdrant,
ports: [
"6333:6333",
"6334:6334",
],
volumes: [
"qdrant:/qdrant/storage"
],
deploy: {
resources: {
limits: {
cpus: '1.0',
memory: '256M'
},
reservations: {
cpus: '0.5',
memory: '256M'
}
}
},
},
},
}

View file

@ -1,11 +1,17 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local url = import "url.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local prompts = import "prompt-template.jsonnet";
{
"chunk-size":: 250,
"chunk-overlap":: 15,
"embeddings-model":: "all-MiniLM-L6-v2",
"graph-rag-entity-limit":: 50,
"graph-rag-triple-limit":: 30,
"graph-rag-max-subgraph-size":: 3000,
services +: {
"pdf-decoder": base + {
@ -32,13 +38,13 @@ local url = import "url.jsonnet";
chunker: base + {
image: images.trustgraph,
command: [
"${CHUNKER:-chunker-token}",
"chunker-token",
"-p",
url.pulsar,
"--chunk-size",
"250",
std.toString($["chunk-size"]),
"--chunk-overlap",
"15",
std.toString($["chunk-overlap"]),
],
deploy: {
resources: {
@ -82,7 +88,7 @@ local url = import "url.jsonnet";
"-p",
url.pulsar,
"-m",
"all-MiniLM-L6-v2",
$["embeddings-model"],
],
deploy: {
resources: {
@ -151,11 +157,11 @@ local url = import "url.jsonnet";
"--prompt-response-queue",
"non-persistent://tg/response/prompt-rag-response",
"--entity-limit",
"50",
std.toString($["graph-rag-entity-limit"]),
"--triple-limit",
"30",
std.toString($["graph-rag-triple-limit"]),
"--max-subgraph-size",
"3000",
std.toString($["graph-rag-max-subgraph-size"]),
],
deploy: {
resources: {
@ -173,5 +179,7 @@ local url = import "url.jsonnet";
}
}
} + prompts

View file

@ -1,5 +0,0 @@
{
pulsar: "pulsar://pulsar:6650",
milvus: "http://milvus:19530",
qdrant: "http://qdrant:6333",
}

View file

@ -1,8 +1,15 @@
local base = import "base.jsonnet";
local images = import "images.jsonnet";
local url = import "url.jsonnet";
local prompts = import "../prompts/gemini.jsonnet";
local base = import "base/base.jsonnet";
local images = import "values/images.jsonnet";
local url = import "values/url.jsonnet";
local prompts = import "prompts/gemini.jsonnet";
{
"vertexai-model":: "gemini-1.0-pro-001",
"vertexai-private-key":: "/vertexai/private.json",
"vertexai-region":: "us-central1",
"vertexai-max-output-tokens":: 4096,
"vertexai-temperature":: 0.0,
services +: {
"text-completion": base + {
@ -12,13 +19,15 @@ local prompts = import "../prompts/gemini.jsonnet";
"-p",
url.pulsar,
"-k",
"/vertexai/private.json",
$["vertexai-private-key"],
"-r",
"us-central1",
$["vertexai-region"],
"-x",
"4096",
std.toString($["vertexai-max-output-tokens"]),
"-t",
"0.0",
std.toString($["vertexai-temperature"]),
"-m",
$["vertexai-model"],
],
volumes: [
"./vertexai:/vertexai"
@ -44,13 +53,15 @@ local prompts = import "../prompts/gemini.jsonnet";
"-p",
url.pulsar,
"-k",
"/vertexai/private.json",
$["vertexai-private-key"],
"-r",
"us-central1",
$["vertexai-region"],
"-x",
"4096",
std.toString($["vertexai-max-output-tokens"]),
"-t",
"0.0",
std.toString($["vertexai-temperature"]),
"-m",
$["vertexai-model"],
"-i",
"non-persistent://tg/request/text-completion-rag",
"-o",
@ -76,4 +87,3 @@ local prompts = import "../prompts/gemini.jsonnet";
},
} + prompts