Deploying the Builder UIs
The Prompt Builder is not required, but it is the no-code tool that puts an otherwise code-only workflow in front of the people who own the work. It is one application that builds up to three kinds of things, chosen on its Setup card:
- a prompt template for LLMs lets prompt engineers test new prompts across LLMs, compare the results, version their experiments, and turn the best prompt into a model for further consumption in the platform (for example in SAS Intelligent Decisioning);
- a decision template for decision models does the same for the typed questions a decision model answers — see Decision Definitions;
- a RAG setup lets a data or decision engineer configure a document pipeline — sources, chunking, embedding model, vector store — save it as a governed RAG setup, and generate the ingestion job and SAS Studio flow that run it. This part is the RAG Builder.
One single-file build, one Job Execution definition and one Data-Driven Content object serve all three. Which of them a deployment offers is decided by three switches among the object's options (see Configuration): decision templates are on by default, prompt optimization and RAG are off until an administrator switches them on. RAG additionally needs its runtime in SAS Content — see RAG Ingestion and Retrieval.
The Prompt Builder is shipped as a standalone application in the LLM-Prompt-Builder directory of this repository. It has no dependency on the SAS Portal Framework for SAS Viya — it is a single self-contained HTML file that you embed directly in a SAS Visual Analytics report through SAS Job Execution. Up to release 2.2 the RAG Builder was a second application with a build, a job definition and a report of its own; all three are retired, and a site that still has them follows the upgrade steps of the 2.3.0 changelog.
Install or upgrade with the transfer package
The content folder /SAS Agentic AI Accelerator ships as one transfer package, SAS-Agentic-AI-Accelerator.json in the folder SAS-Viya-Integrations of the repository. It holds the Prompt Builder ready to run — the Visual Analytics report that hosts the object, the SAS Job Execution definition that serves the single-file app and the optimization job — and with it the RAG runtime, the logging programs with their two custom steps and the usage report, the files the tables of the reports are loaded from, and a first page with an installation check. Importing it takes the place of building the app and wiring it up by hand (steps 1–2 below).
The commands of this section ran on the test system in eleven passes, twice on 2026-10-12, three times on 2026-10-13, three times on 2026-10-14, twice on 2026-10-15 and once on 2026-10-16, in test content roots and as they were then: what the first found changed the import, the second measured the import in place, the third what was built after the second, the fourth what was built after the third, the fifth what was changed after the fourth, the sixth what was changed after the fifth, the seventh what was changed after the sixth, and each pass after it what was changed after the pass before. What was measured, and what has not run has all eleven.
First install and upgrade
With mdb installed and your .env filled in (Setup SAS Model Manager):
# first install
mdb setup # the repository, the projects, the release table, the authorization commands of the repository
mdb builders-import # the content folder
mdb check # the installation check, by part, with what to do
# upgrade
mdb builders-import
mdb check
A site that created the Call LLM node of SAS Intelligent Decisioning with Create-Custom-SAS-Intelligent-Decisioning-Node.sas of an earlier release has it under an id of its own, and the package brings it under f9efcbb7-f57b-4a62-b56d-1a89595e2039. There mdb builders-import asks the site before it uploads anything of the decisions: it leaves the decision Decisions/Call LLM Node and the node type out, imports everything else, and ends with exit code 1 and the command to run once, before the import:
mdb decision-node-migrate # dry run: the node type and every decision version that uses it
mdb decision-node-migrate --apply # give the node the id of the package and move those decisions
mdb builders-import
The Call LLM node and its id says what the migration does and why the old node is never deleted while a decision uses it; --no-decisions imports without the decision instead.
Then run mdb check. It prints the installation check by part, with what to do, and writes it into the page Start Here:
https://<your SAS Viya host>/SASJobExecution/?_program=/SAS Agentic AI Accelerator/Start Here
Go through the lines of the check, run the commands of Access to the content folder, and — on a first install — continue at step 3 (the credential domain), step 4 (the Content Security Policy) and the optional object options of step 5.
mdb builders-import does in one run what an import otherwise leaves to hand work afterwards:
- It reads the options of the Prompt Builder report at its place,
<content root>/Prompt Builder/Prompt Builder— and of the retired report<content root>/RAG/RAG Builder, while that still exists — and keeps them in a filebuilder-options-captured-<time>.jsonin the current directory. A report of that name elsewhere is not read and not written. Importing a report replaces the options an administrator set on the object, see Preserving builder options across a report import; this is what makes an upgrade one command. A report that is there and cannot be read, or that holds no option at all, stops the command here, before anything is loaded or imported. - It loads the release table the Prompt Builder report binds to (
--no-releasesskips that): fromData/ACCELERATOR_RELEASES.csvof the package where the package holds it, so that the table says what was imported, and otherwise from the changelog of the checkout. - The reports go through the SAS Viya transfer service: the two reports, the files that are children of a report (the pictures of the usage report) and the folders they sit in, cut out of the package. The command rewrites the import mapping before the job starts, so that nothing is edited after the fact: the data source of the Prompt Builder report is retargeted from the shipped
Public.ACCELERATOR_RELEASEStoSAS_CAS_LIBRARY.SAS_RELEASES_TABLEon your CAS server, and the placeholder hosthttps://your-sas-viya-hostin the Data-Driven Content URL becomesSAS_VIYA_URL. With--report-caslib <library>the other tables the reports read —LLM_LOGS,LLM_FACT_SHEET,EMBEDDING_FACT_SHEET,PROMPT_EXPERIMENTS— are pointed at the tables of the same name in that library; without it they stay bound toPublic. It waits for the job and reads every task of it: a task that did not endcompletedis printed and ends the run with exit code 1. - The decision
Decisions/Call LLM Nodeand the Call LLM node type it carries go through the transfer service too, in an import job of their own (--no-decisionsleaves them out). The command asks the site first: a node type with the id of the package is overwritten in place - local edits of its code and its terms are replaced, and the line says so -, a missing one is created under that id, and a Call LLM node under another id stops this part before anything of it is uploaded, with exit code 1 and the linemdb decision-node-migrate, because the transfer service would skip the decision without a failed task. A decision or a node type without a completed task of the job ends the run with exit code 1. A decision that is not imported gets no folder in place either. See The decision that carries the Call LLM node. - Every file, custom step and job definition is written in place, by the requests
mdb content-deploysends, with the package as the source: an object that is at its path on your site is updated under the id it has there, and a missing one is created. One line per object saysequal,updated,createdorFAILED— see What an upgrade does to what a site has. Every file, custom step and job definition below the root folder of the package is brought, and every empty folder — the manifest of your checkout does not limit what is brought. The never-ship rule and the rule for a path do: an object whose name, or the name of a folder above it, is empty,.or.., holds a slash or a blank at either end is not taken, and gets anot imported:line with its reason.release.jsonis written last, and only when every object in front of it was written. - It writes the options back: the ones it read; or, with
--options <file>, a file saved withmdb options-save— the way to change a value before the import; or, on a site that has no Prompt Builder report yet, the valuesmdb setupdiscovers — the repository, the LLM and the embedding project ids, the SCR endpoint and the deployment type. On such a first install the three switches keep the defaults of the package, so RAG and prompt optimization are off until you enable them in step 5. Either way every placeholder host that is left in the report becomesSAS_VIYA_URL. A repository or project id the report holds no value for is discovered from the deployment, for that kind only; a value the report holds is kept. A run that writes every option back removes itsbuilder-options-captured-<time>.json. - It writes
sas-viya-cli-content-commands.txt, see Access to the content folder.
mdb builders-import --dry-run says what a run would do and writes nothing: every request it sends, the sign-in aside, is a GET, nothing is uploaded, and its mapping: lines are what the package itself says about its tables and its host. A run that did not do everything ends with exit code 1 and prints the command to run next, on a line of its own; that command names the kept options file, so that the second run does not read the options again from a report the import may have replaced already. A kept file stays in the directory the command ran in until a run writes every option it holds back. It holds option values and no key. The release table needs no clone, the rest of the command does: the clone of the repository gives the manifest — the default package file and content root, and the site settings of the job definitions it lists —, configures a site without the Prompt Builder report by discovery from the model definitions, and gives the release table of a package that holds none.
Where a run stops:
- A report that is there and cannot be read:
<report>: it is on this site and its content cannot be read (...),Nothing was imported.A report that is read and holds no option at all stops the same way:<report>: it is in <folder> and holds no option at all - .... Run the command again when the report can be read, name the options with--options <file>, or pass--no-options-capture— then the imported report is configured from the deployment, and what an administrator set is gone. - A task of the import job that did not end
completed:the import job ended completed, and n of its tasks did not:with a line per task. The options are not written into a report the command cannot be sure was replaced; the command to repeat names the kept options file. - A write of the in-place part that is refused or gets no answer: that object is
FAILED, every object behind it is listed asnot attempted, andStopped at the first write that failed: ...says what was written before it. A folder that cannot be created stops the run the same way. Nothing is removed, and a second run leaves what is equal by then as it is.release.jsonis written last, and only when every entry in front of it was written: after a run that stopped, the folder'srelease.jsonis the one an earlier run wrote — Start Here shows that release and its commit, and checks the entries that file lists —, and after a first install that stopped there is none, and Start Here showsrelease.jsonas missing.
What an upgrade does to what a site has
A custom step that a flow uses is referenced by its id, and so is a job definition behind a link or a schedule. The in-place part of the import therefore never replaces an object by a new one:
updated step RAG/steps/RAG - Embed Chunks.step (templates; id <the id on your site> kept)
updated file RAG/rag_core/tokens.py (content replaced in place; id <the id on your site> kept)
updated jobDefinition Start Here (code; id <the id on your site> kept)
equal step RAG/steps/RAG - List Documents.step (id <the id on your site>)
created file Logging and Monitoring/Link-Logs-To-Models.sas (id <a new id>)
- An object at its path is updated under its own id, whatever id the package carries for it — a site that got its content from an earlier package, from
mdb content-deployor from the earlier deploy script has other ids than the system the package was exported from. A file keeps its id too: its content is replaced, the file is not deleted. Nothing of the exporting system — an id, a link, a user — reaches your site with the in-place part. - A second import changes nothing: every line says
equal, and nothing is written. - Two objects of one name in one folder are both named with their ids, and neither is written; the same holds for an object and a reference of its name to an object of its kind elsewhere, and such a reference alone fails its entry. A reference of that name to an object of another kind fails nothing: the run lists it at the end as
<path> (reference to a <kind> - not the <kind of the entry> that is listed under this name). The kinds are spelled as the other lines spell them: file, folder, report, registered step, job definition; an object of no kind of the manifest is called by the content type the Folders service gives it, or by its address. The rest is brought and the run ends with exit code 1. Remove the one that is not used, or the reference, and run the command again. - What is not at its path is not touched. A step you registered in a folder of your own — the starter package of earlier releases put LLM - Log Parser into the folder
Custom Steps, and a step may live in the folder of a user — stays as it is, and the import creates the step of this release at its path beside it. Update yours in place from a checkout withmdb content-deploy --only "<its path in the manifest>" --to "<path or id of your step>", see One part, somewhere else. - The settings of a site in a job definition stay. A compute context a site set in the parameter
_contextNameof a job definition —Load-Accelerator-Tables,SAS-LLM-Prompt-Builder-UI— stays: an import and a deploy leave an existing value alone. The option RAG: ingestion compute context (ragComputeContext) of the options the import writes back is given to a job definition the import creates and to one on the site that has no_contextName; a_contextNamea job definition on the site holds is kept. It has to be a context that runs as the requesting user; where the options name none, the default of the package (SAS Studio compute context) takes its place, andcomputeContext, the context of the optimization job, is not taken.--compute-context <name>writes that context into every job definition that has the parameter.contentRootis always written with the content root the content goes to. The other values a site pointsStart Hereand the loader at — the CAS library and server, the credential domain, the names of the repository and the projects, the compute contexts the page looks for, the release table — are settings of the site as well; the address of the guides is the release's. Measured in the second pass of 2026-10-12: the import kept the_contextNamea test root had set onStart Hereand wrote that root into itscontentRoot. Measured on 2026-10-13, with options that name an ingestion compute context:Start Herekept the_contextNamethe root had set, the job definitionLoad-Accelerator-Tables, which the import created, got the context of the options, and--compute-contextwrote its context intoStart Here. A job definition on the site without the parameter was not part of that pass.
Another content folder. --content-root <folder> brings everything but the reports into that folder and writes it into the parameter contentRoot of the two job definitions that have one; a folder that is not there is created only with --create. The reports and the decision Decisions/Call LLM Node are imported only into the folder the package was exported from, /SAS Agentic AI Accelerator, and only because that folder is at the root of SAS Content: the transfer service takes no target folder, and no mapping to another folder is known to work. With another content root the command says Reports (...): NOT imported and Decisions (...): NOT imported with the reason and ends with exit code 1; --no-reports --no-decisions says that this is meant, and the transfer service is then not called at all. The command it names to run again adds both.
After an upgrade, reload the Prompt Builder report. On a site that had the report RAG Builder, the switch Enable RAG setups in the Options pane of the object is ticked and the RAG: options below it hold the values of the old report; the Setup card offers RAG setup for a document collection as a third kind, and an existing setup opens. If the RAG card is shown and cannot be chosen, the credential domain is none — see The retired RAG Builder report. Enable prompt optimization (DSPy) shows what the report stored: ticked where optimization was enabled before.
What an import does not bring, and what it replaces:
- No tables, no projects, no credentials, no option values. A transfer package cannot carry CAS tables, SAS Model Manager projects, credentials or the option values of a site.
mdb setupcreates the repository and the projects, the command above writes the options, Managing Credentials has the credential domain, and the tables are loaded as The tables of the reports says. - A job definition holds a copy of its program and of its form. An import replaces both, and the parameters with them, except the two that are settings of a site: a compute context an administrator set in the parameter
_contextNameofSAS-LLM-Prompt-Builder-UIstays. The job definition of a RAG setup that a site generated is not part of the package and keeps the ingestion program of the day it was manifested — see After an upgrade: manifest every setup once. - The logging programs are replaced as files. Settings that were written into
Log-Parser-Code.sasorGet-All-Prompts.sasare gone with the old file; from release 2.3.0 on a site sets them in a program of its own — see Settings that stay yours. - An import removes nothing. What a site has in the folder beside the content of the package stays, the retired report
RAG Builderamong it. - The Call LLM node comes under one id,
f9efcbb7-f57b-4a62-b56d-1a89595e2039, with the decision that carries it. A site that has a node named Call LLM under another id — created withCreate-Custom-SAS-Intelligent-Decisioning-Node.sasof an earlier release — runs the migrationmdb decision-node-migratebefore the import.mdb builders-importrefuses the decisions there before it uploads them and ends with exit code 1 and the migration command. A transfer import of the package in SAS Environment Manager is not asked: it fails the task of the node, and skips the decision without a task, while the import job endscompleted. See The Call LLM node and its id. - The reports go into one folder. The files, custom steps and job definitions follow
--content-root; the two reports and the decision are imported into/SAS Agentic AI Acceleratoror not at all, see What an upgrade does to what a site has. - The uploaded package is deleted. The command uploads the reduced package for the reports and deletes it once the import job has ended; a delete that is refused is a line with the id of the package and how to delete it. That delete has not run against SAS Viya.
The content folder
What the package brings, as SAS-Viya-Integrations/package-manifest.yaml lists it — one entry per object, with the repository file it is a copy of:
Path below /SAS Agentic AI Accelerator | What |
|---|---|
Start Here | Job definition: the first page - the links, and the installation check mdb check writes into it |
release.json | The release, the commit the folder was deployed from, and every object of the folder with a hash of its content |
Prompt Builder/ | The report Prompt Builder; the job definitions SAS-LLM-Prompt-Builder-UI (the single-file app) and Optimize-Prompt-DSPy; Create-Optimization-Dataset.sas |
RAG/ | steps/ with the nine RAG custom steps, rag_core/, jobs/ with Ingest-Documents.sas and Test-Retrieval.sas, models/ with the retrieval model template |
Logging and Monitoring/ | The report LLM Usage Report with its images; the custom steps LLM - Log Parser and LLM - Get All Prompts; Log-Parser-Code.sas, Get-All-Prompts.sas, Link-Logs-To-Models.sas, Build-RAG-Cost-View.sas, Load-Fact-Sheets.sas |
Data/ | llm_fact_sheet.csv, embedding_fact_sheet.csv, decision_fact_sheet.csv, ACCELERATOR_RELEASES.csv; the job definition Load-Accelerator-Tables |
Custom Steps/ | LLM - List Available LLMs, LLM - Get Options, LLM - Create LLM Call and the four MM - steps |
Decisions/ | The decision Call LLM Node: it holds only the Call LLM node and brings that custom node type of SAS Intelligent Decisioning under the id f9efcbb7-f57b-4a62-b56d-1a89595e2039. It is not meant to be published - see The Call LLM node and its id |
62 objects: 37 files, 18 custom steps, four job definitions, two reports and one decision, which carries the Call LLM node type as its dependency. The folder names are the ones sites have had; Start Here, release.json and Data/Load-Accelerator-Tables are new in release 2.3.0, and so is the folder Custom Steps for a site that never imported the starter package of earlier releases. That package held LLM - Log Parser in Custom Steps; the step of this release is in Logging and Monitoring.
LLM - Get Options ships, although its program is an earlier copy of LLM - Create LLM Call with the same lookup of the options and the same generated code: the repository lists it as a step of the accelerator, and a flow that holds it keeps its step after an import. LLM - Create LLM Call is version 1.7, and LLM - Get Options 1.6: the code clears the fileref llmIn of the temporary file of the request right behind the call - also behind a call that failed -, and SAS deletes the file, which holds the provider key where the options have API_KEY; versions 1.6 and 1.5 left it in the WORK folder of the SAS session until the session ended. Measured on 2026-10-20 with the code each of the two steps and Create-LLM-Call.sas of this release wrote for gpt_5_mini, run in a session of the SAS Studio compute context with a stand-in in the place of the key: before the call the request file held the stand-in, behind it the fileref was not assigned and the file was gone - for a call whose host did not resolve (an ERROR line of PROC HTTP, which sets SYSCC 3000) and for a call answered with HTTP 404 (SYSCC 0) -, and the WORK folder held no temporary file behind the code of the script, only the catalogs, the registry and the lock file of the session. Versions 1.6 and 1.5: the code for a model whose options file has the option API_KEY reads the provider key from the credential domain when it runs, from the entry the option names, and sends it in the place of the name - see Managing Credentials. The domain is the one of the line %let API_KEY_DOMAIN=agentic-ai-keys; of the generated code, which the steps write as it stands and do not take from the Credential domain option of the Prompt Builder: a site whose keys are in another domain edits that one line. Versions 1.5 and 1.4 wrote %let API_KEY=<entry name>; and their code sent the name as the key: measured on 2026-10-20 for gpt_5_mini, the container answered 422 and its log held the 401 of the provider, while the step had ended without a word. Measured the same day with versions 1.6 and 1.5 and the program: the NOTE of the step, the key read (NOTE: The key of the entry OpenAI was read from the credential domain agentic-ai-keys.) and gpt_5_mini answering with HTTP 200 - also for code written to SAS Content and included from inside a macro -, no value of the domain in the log, and for an entry the domain does not hold the WARNING and HTTP 422. Versions 1.5 and 1.4: an options file without any option - the options file of an LLM that reads its key from its container (mdb add --auth env) can be {} - gets code that sends an empty options object, where the versions before wrote no code for it and ended with an ERROR line (read from the program and followed by the tests, not run in SAS). Version 1.4: with Save the code to a filename? ticked and no path chosen it writes the code to a temporary file and leaves the fileref _slo_LLM assigned, where version 1.0 held an ERROR line and an ABORT statement for that; measured with version 1.1 on 2026-10-12 for a model whose options.json has no values list. For a model whose options.json holds one, version 1.1 failed in PROC TRANSPOSE and wrote its code without that option; version 1.2 reads the options with one PROC SQL step, and a run that cannot read every default writes no code and ends with an ERROR line and SYSCC 1012. Measured with version 1.2 on 2026-10-13, with the values of manual entry: for gpt_5_mini a code file of 34 lines with %let reasoning_effort=medium; and SYSCC 0, for gpt_41_mini 36 lines, and for an options file that is not there the ERROR line and SYSCC 1012. LLM - Get Options 1.1, changed the same way, failed that day with the box ticked and no path, the values it comes with: it wrote the code file (34 records) and then ended with ERROR 180-322, SYSCC 3000 and a failed job, because its closing NOTE resolved the statement that assigns the temporary file with its semicolon. Version 1.2 keeps that statement without the semicolon and reads it in the NOTE through %SUPERQ. Both steps now write a path into the statement that assigns the file with the QUOTE function, so an apostrophe in a folder or file name is doubled where it ended the text of the statement before. Measured in the second pass of 2026-10-13 with LLM - Create LLM Call 1.3 and LLM - Get Options 1.2: with a model entered by hand, the box ticked and no path, each step wrote its code file - 34 lines for gpt_5_mini, as a program and through the code SAS Studio generates for the step - and ended with SYSCC 0, and LLM - Get Options wrote its closing NOTE on one line; with a path in SAS Content, and with a path of the form sasserver:/tmp/<folder>_o'brien/call.sas, both wrote the file there. A quotation mark in a path reaches the DATA step whole: measured in that pass with the code SAS Studio 2026.09 generates for a path control that holds sasserver:/tmp/zz_verify_r20a_p;q'r"s&t%u/call.sas - SAS Studio writes the value masked, %nrquote(%nrstr(...)) with a percent sign in front of each quotation mark -, a DATA step that read it as "&_slo_fl_path.", the way both steps read it, held the 50 characters of the path, and SYMGET gave the same 50; the steps were not run with such a path. Two defects of that pass are changed in LLM - Create LLM Call 1.4 and LLM - Get Options 1.3. With Select name from a table and a model table whose column optionsFileURI was 200 characters wide, both steps asked for <uri><blanks>/content, got HTTP 404 and wrote no code, in 3 of 3 runs, where a column of 49 characters, which is what LLM - List Available LLMs writes, gave the code: the URI is read with TRIMMED now. And run as they come - the selector on table, no table connected, no model name selected - they wrote warnings of macro variables that were not resolved and a syntax error of PROC SQL, and then named an empty options file and HTTP 404: a run without a model name or without an options file now asks SAS Viya for nothing and ends with one ERROR line that says that no model with an options file was chosen, and SYSCC 1012. Measured in the third pass of 2026-10-13 with versions 1.4 and 1.3: as they come, through the code SAS Studio generates for the steps, each wrote no WARNING, ran neither PROC SQL nor PROC HTTP and ended with that one ERROR line and SYSCC 1012; with a model table whose column optionsFileURI is 200 characters wide both wrote their code file - 34 lines for gpt_5_mini, 36 for gpt_41_mini - and ended with SYSCC 0, in 4 of 4 runs. The script Create-LLM-Call.sas of SAS-Code-LLM-Calls, a third copy of that program, has the same lines since that pass and ends a run without a model name and an options file the same way. Measured on 2026-10-14 as a program: as it comes, with a model name alone and with a URI alone, no request, that one ERROR line of the script and SYSCC 1012; with both, for gpt_41_mini, 36 lines of code and SYSCC 0. MM - Get Model Information 1.1 took out the statement at the end of its program that deleted the macro of MM - Get Models in Project (not run in SAS).
MM - Get Model Information is version 1.2: it reads a model with the properties it has, and writes a table only for a part the model has. Version 1.1 transposed a fixed list of properties, and for an LLM of the accelerator, which lacks six of them (trainTable, targetEvent, candidateChampion, publishTimeStamp, externalModelId, sasOptions), it ended with an ERROR line for each, SYSCC 3000 and tables without rows - measured on 2026-10-20 with a model table and with an ID entered by hand. A model that was never published has no published models: the step then writes a NOTE in place of work._mgi_publishedModels, where 1.1 named a table that is not there. The tables of an earlier run are deleted first, and a run without a model ID, or for one SAS Model Manager does not answer with HTTP 200, ends with one ERROR line. MM-Get-Models-Information.sas of SAS-Viya-Integrations/SAS-Code-Model-Manager-Interaction is changed the same way. Measured on 2026-10-20 with version 1.2 and the program: six tables for gpt_5_mini, five and the NOTE for an LLM that was never published, 37 properties for a model of SAS Model Studio, SYSCC 0 for each, and the ERROR line with status 404 for an unknown ID.
What never ships. An object that the manifest does not list is not deployed, not exported and fails the check of the package when it is found in it. And whatever the manifest says, an object is refused where a part of its path has Showcases or Example in its name, is a folder named generated, or starts with ZZ_VERIFY:
- showcase and example content — it lives outside the product, for instance in a folder
/SAS Agentic AI Accelerator Showcasesof a site, and is not in the repository; RAG/generated, where the Prompt Builder writes the ingestion job and the flow of a site's own RAG setups;- test objects.
Not in the package either: the retired report RAG Builder and its job definition RAG-Builder-UI; model definitions, which reach a site with mdb register; the log collection script, which runs where kubectl runs; and the custom step LLM - Get Prompt Experiments of the repository: it is the step of August 2024 that reads the experiment tracker of one model as one table and shows the options of a run as they are stored, where LLM - Get All Prompts pulls the experiments of every model and leaves out the options whose names say that they hold a secret. Its file stays in Custom-Steps/ of the repository.
A site that takes a fix from a checkout without waiting for a package uses mdb content-deploy, and maintainers build the package with it — see The content folder: deploy, export, check.
Start Here: the first page and the installation check
Start Here is a job definition at the root of the content folder that serves one page. Opening it runs nothing - no compute session, no request -, so it opens on a site whose compute server is not set up yet:
https://<your SAS Viya host>/SASJobExecution/?_program=/SAS Agentic AI Accelerator/Start Here
Until the first check the page holds what the release ships: what the accelerator is, the guides, and that mdb check is to be run. mdb check writes its result into the page: the table By part, every line of the check with what was found and what to do, the links to the Prompt Builder report and the LLM Usage Report by the address they have on your site, and when it ran. mdb builders-import and mdb content-deploy write the page of the release back; run mdb check after them.
Every line of the check has one of four states — ok, missing, not checked, not used — with what was found and one sentence what to do. Every line belongs to one part of the accelerator - prompt building, logging and the LLM Usage Report, RAG, prompt optimization, decision models -, and the table By part gives each part one verdict: ready, the number of lines to do, the number not checked, or not used here, with the first thing to do. A part the options of the Prompt Builder report switch off (enableRag, enableOptimization, enableDecision) is not used here, and so is the driver of a RAG backend the options do not offer (enable_pgvector, enable_singlestore): their lines that are not ok are not used rather than missing. Where the options cannot be read, every part counts as used. The sections:
| Section | What is looked at |
|---|---|
| Release | release.json in the content root: release, commit and date |
| Content | Every entry release.json lists: present in this content root or not, told the way mdb content-deploy tells it - a member has the name of the entry to the letter, a reference is no entry and is not followed, and a reference of the name and kind of an entry or two objects of that name and kind stand in its way. A custom step is registered by the import or by mdb content-deploy; a .step file uploaded to the folder is no step. For a report or the decision that is missing the advice names the import of the package and mdb builders-import |
| CAS tables of the reports | ACCELERATOR_RELEASES (Prompt Builder) and LLM_LOGS, PROMPT_EXPERIMENTS, LLM_FACT_SHEET, EMBEDDING_FACT_SHEET, DECISION_FACT_SHEET, RAG_SETUPS, RAG_RUNS (LLM Usage Report): there or not, their rows, when they were loaded |
| CAS tables of the logging chain | The two tables Get-All-Prompts.sas writes for Link-Logs-To-Models.sas that no report binds: PROMPT_OPTIMIZATIONS, MODEL_CATALOG |
| SAS Model Manager | The repository and the projects of the LLM, the embedding model and the decision model definitions, by their names, with the number of models of each; a project without a model is missing |
| Options of the Prompt Builder report | modelRepositoryID, llmProjectID and, with RAG switched on, embeddingProjectID: whether each names the object that was found by its name |
| Credentials | The credential domain of the provider keys, by its name. No entry of it is asked for |
| Publishing destinations | Whether one is defined, and their names |
| Compute contexts | The contexts the options computeContext and ragComputeContext of the Prompt Builder name, and the ones the parameter computeContexts names |
| Compute server | For each compute context a part runs its jobs in: whether a session starts, whether PROC PYTHON runs, whether the compute server reaches SAS Viya and trusts its certificate - with the cause where it does not: a certificate it does not trust (A program or custom step fails with CERTIFICATE_VERIFY_FAILED), a host it cannot reach, a timeout -, and for RAG and prompt optimization, whose jobs run as the person who starts them, whether its sessions run as you |
| Python packages | For each part what the Python of the compute context it runs in needs: pandas, swat and requests for logging; for the RAG runtime also psycopg2-binary, singlestoredb, pypdfium2 and markitdown; dspy, requests and optuna for prompt optimization |
The parameters of the job definition are the settings of the check, and settings of the site - a deploy and an import leave them as the site has them: the content root (contentRoot), the CAS library and server of the report tables (caslib, casServer), the credential domain, the repository and the three projects by the names mdb setup gives them, the compute contexts to look for (computeContexts; the first is the one the compute server is probed in for prompt building and logging), and the address of the guides (guideUrl, the release's). The guides are links only on https://sassoftware.github.io or on this SAS Viya. SAS-Viya-Integrations/Setup/README.md lists them.
mdb check: the installation check from the command line
mdb check [--content-root <folder>] [--compute-context <name>] [--no-deploy] [--no-page] [--all] [--json]
mdb check runs the installation check as you, prints it, and writes it into Start Here:
- It brings the job definition
Start Hereto the state of the checkout, in place, asmdb content-deploy --only "Start Here"does: the id and the settings of the site stay.--no-deployleaves it as it is. - It reads SAS Viya with GET requests: the sections above, but the compute server.
- It probes the compute server: in each compute context a part runs its jobs in it starts one compute session, runs a short program of its own - PROC PYTHON, the packages, one request to the address of
SERVICESBASEURL, the user of the session - and deletes the session. Prompt building and logging are probed in the context--compute-contextnames, else the first ofcomputeContexts; RAG and prompt optimization in the ones their options name, where they are switched on. Where mdb runs withSAS_VIYA_VERIFY_SSL=false(in.env, say), the probe sets it in its compute session too and asks with verification off, as mdb does; the line of the compute server says so. - It prints the verdict of each part and, for every line that is not ok, what was found and what to do (
--allprints the lines that are ok as well,--jsonall of it), and writes the page into the form ofStart Here- its program, parameters and id stay.--no-pageleaves the page as it is.
Measured on SAS Viya 2026.10 on 2026-11-03, for a test content root that mdb content-deploy had filled - it holds no report, which only the package brings:
/ZZ_VERIFY mdb check: 107 ok, 3 missing, 1 not checked.
Prompt building 3 to do
missing Prompt Builder/Prompt Builder (report): not in its folder
what to do: Import the package of the accelerator again, or run mdb builders-import: a report comes with the package, and mdb content-deploy writes none.
...
Logging and the LLM Usage Report ready
RAG ready
The page Start Here holds this check now.
On that system the probe of the SAS Studio compute context found PROC PYTHON, every package and answered HTTP 401: the compute server reaches SAS Viya and trusts its certificate; with --compute-context "SAS Job Execution compute context", whose sessions run as another user there, RAG and prompt optimization each had 1 to do: the line that names the option to point at a context that runs as you. The page opened through SAS Job Execution as written, with the table By part and no script. A compute server that does not trust the certificate of SAS Viya is tested with the program of the probe, not measured on a system.
Where the compute server did not reach SAS Viya, mdb, which did, says that the cause lies between the compute server and SAS Viya, not in the installation. The exit code is 0 when prompt building is ready, 1 when it is not, and 2 when the command could not start. The command changes nothing but the page: its requests to the services are GETs, and the compute sessions it starts are deleted when they have answered.
The tables of the reports
A package cannot carry a CAS table. The two reports read five:
| Table | Report | Loaded by |
|---|---|---|
ACCELERATOR_RELEASES | Prompt Builder | mdb setup, mdb builders-import and mdb load-releases; or the job Load-Accelerator-Tables |
LLM_FACT_SHEET, EMBEDDING_FACT_SHEET, DECISION_FACT_SHEET | LLM Usage Report | mdb load-facts; or the job Load-Accelerator-Tables, which loads the sheets of the release |
LLM_LOGS, PROMPT_EXPERIMENTS, RAG_SETUPS, RAG_RUNS | LLM Usage Report | The log parser and the pull of your own system, see Logging & Monitoring |
Data/Load-Accelerator-Tables is a job definition for a site that imported the package without mdb — through SAS Environment Manager, for example — and so has neither the release table nor the fact sheets: it loads ACCELERATOR_RELEASES from Data/ACCELERATOR_RELEASES.csv and the three fact sheets from the files beside it, promoted and saved, into the CAS library of its parameter caslib (default Public). Its other parameters are _contextName - a compute context that runs as you -, contentRoot and releaseTable, the name of the release table, and _output_type with the value html, for its page.
- Started from its address in SAS Job Execution, the job answers with a page: each of the four tables with its rows and what this run did with it - Loaded by this run, Left as it was or Not in the CAS library - under a line that says whether the four are in the CAS library. Whether this run loaded a table is read from CAS: the rows of each table and the time CAS made it are read before the tables are loaded and again at the end, and a table with another time, or one that was not there before, is one this run loaded. The log has every line. SAS Job Execution answers a run that fails with its own page Job Error, which names the ERROR line of the job. A table the upload could not take the place of - two runs at the same time, where the promote of the second is refused because the first loaded the table - is Left as it was, Loaded again from its saved file or Loaded by this run and not saved, with its WARNING in the log; the job then ends with warnings, not as a failed job, so the page shows (measured on 2026-10-20: two runs started 3 seconds apart, the second refused with
The target table DECISION_FACT_SHEET of the promotion already exists, ended with Job Error before this change). A job definition without_output_type=htmlgets no page, with a NOTE, and the program run in SAS Studio writes none. Measured on 2026-10-20 in a test folder with test table names: four rows Loaded by this run for a run that loaded the four tables, the release table Loaded by this run and the three fact sheets Left as it was for a content root withoutdecision_fact_sheet.csvandLoad-Fact-Sheets.sas, and Job Error with the ERROR line for a run without the release file. Before, the job answered with an empty page SAS Output: its job definition had no_output_type, and what it loaded was in its log only. - The fact sheets are loaded by
Load-Fact-Sheets.sas, which the job includes from the folderLogging and Monitoringof the content root. - The fact sheets in
Dataare the ones of the release. The job replaces a fact sheet table with them, and with it the rows of definitions a site keeps of its own. Such a site loads its sheets withmdb load-factsand starts the job withfactSheets=0: it then loads the release table only, and a fact sheet that is not in the CAS library is a WARNING instead of a failed job. - The release table gets the eleven columns of
mdbin their order, the three numbers as DOUBLE and the eight text columns as VARCHAR. Measured on the test system on 2026-10-12 against the tablemdb load-releasesloaded: 707 rows, the same eleven columns, types and lengths, and no difference inPROC COMPARE. One thing differs: the formatted length - the width CAS keeps for display - is 16 in the table of the job for the four text columns whose longest value is shorter than that (version,release_date,change_type,builder_part), and 6, 10, 8 and 8 in the table ofmdb. A width of 16 cuts no value of these columns, and the page of the Prompt Builder reads nothing from the table; what a SAS Visual Analytics object shows with it was not measured. - A table that exists is replaced only by an upload that holds every row of its file. A file that is not in the folder, a first line with other columns and an upload that is short leave the table as it is, with a WARNING. So does a folder that is not there. Measured on 2026-10-13 with a content root that had no folder
Logging and Monitoring: the FILENAME statement for the program of that folder wroteERROR: Folder denoted by path ... was not found.andERROR: Error in the FILENAME statement.in front of the WARNING. The job andLoad-Fact-Sheets.sasnow ask the Folders service for a folder first -GET /folders/folders/@item?path=, the request Start Here asks with - and assign no fileref where the answer is 404; every other answer assigns it as before. Measured in the second pass of 2026-10-13 with the job as changed and a content root withoutLogging and Monitoring: PROC HTTP wroteNOTE: 404 Not Foundand no ERROR line, no ERROR line of a FILENAME statement followed, the log held the WARNING thatLoad-Fact-Sheets.saswas not found, and the release table was loaded. The job still ended as failed -ERROR: Load-Accelerator-Tables: not in the CAS library casuser at the end of the run: LLM_FACT_SHEET EMBEDDING_FACT_SHEET DECISION_FACT_SHEET.andSYSCC1012 -, because none of the three fact sheet tables was in that CAS library: the WARNING is what the job writes for the folder, and the check at its end is the last point of this list. - A table that has the name the job or
Load-Fact-Sheets.sasloads a table of its session under -<table>_NEWfor an upload,_LAT_SAVEDfor the saved file of a release table - is not touched. Both programs asktable.tableExistsbefore they load under the name, and load only where CAS answers 0. With another answer nothing is uploaded and both tables stay as they are, with a WARNING that says to rename or drop<table>_NEW; a saved file is then not looked at and is left as it is, with an ERROR line of its own: it names the table_LAT_SAVED, says whattable.tableExistsanswered -tableExists said <n>, and 0 is no table- and that the table is to be renamed or dropped before the job is run again. Where CAS refuses the question for<table>_NEW, or the PROC CAS step that asks it halts, nothing is uploaded either: the job ends withERROR: Load-Accelerator-Tables: CAS did not say whether there is a table <caslib>.<table>_NEW, the name the file is uploaded under first. ...andSYSCC1012, andLoad-Fact-Sheets.saswritesWARNING: CAS did not say whether there is a table <caslib>.<table>_NEW, ...(not run in SAS). Before, the programs loaded under the name withreplaceand dropped it again; whether that can take a global table of a site rests on what CAS does with a session table that has the name of a global one. Measured in the third pass of 2026-10-13, incasuserwith test table names. With a global table<releaseTable>_NEWof 19 rows made first: the WARNING withtableExists said 2, no upload, and both tables with the rows and the created time of before.Load-Fact-Sheets.sas, from a copy with three test table names: the same WARNING for<table>_NEW, that sheet not loaded, the other two loaded. With a table_LAT_SAVEDof the session and a release table that was only its saved file, the job as it was then wrote the ERROR line of a file that could not be loaded, which named a cause that was not the case - the next run, without that table, loaded 796 rows from the same file -; that is what the line of its own is for. A name a session table and a global table both had, in one run:tableExistssaid 1 in the session that held both and 2 in another session; the firsttable.dropTabletook the table of the session, the second the global one. Not measured:_LAT_SAVEDas a global table,Load-Fact-Sheets.saswith the table names it ships with, and the condition code of a run whose only message is the WARNING - every run of the pass ended withSYSCC1012 for the three fact sheet tables its test root lacked. The ERROR line for_LAT_SAVEDas it is now was measured on 2026-10-14 with a table_LAT_SAVEDof the session of 19 rows and a release table that was only its saved file:tableExists said 1,SYSCC1012, that table with its 19 rows, and the saved file with its size and its time. A table of such a name that the session may not see is not found by the question. Load-Fact-Sheets.sasreplaces each fact sheet table the same way: the upload of the sheet whose rows were counted is promoted under the name of the table and saved, and the sheet is not loaded a second time. Where the promote is refused, a table of that name that is in memory - the table could not be dropped - is left as it is; where none is, the table is loaded again from the file it was saved to before. Each of these outcomes, and a load from the file that fails as well, is a WARNING of its own. Where CAS refuses the question whether such a table is in memory, nothing is loaded either; the WARNING then says that the tablewas not loaded again from the file it was saved to before. A sheet whose count CAS refused is a sheet CAS did not take (-1 stands for a table that CAS did not say is there or did not count). A swap whose PROC CAS step halted writesWARNING: Nothing said what became of the sheet - messages above -, the table <caslib>.<table> may not hold it.Not run in SAS. As first built for this release the program dropped its trial and loaded the sheet a second time, so a failure of CAS between the drop and that load left the site without the table. The swap ran on 2026-10-13, from a copy of the program with test table names incasuser: 30, 16 and 5 rows, each table global and saved, no WARNING, one load per sheet, and the same rows after a second run; the program that ran on 2026-10-12 was the one with the second load. In the second pass of 2026-10-13 such a copy ran with the request for the folder: pointed at a folder that is not there it wroteNOTE: 404 Not Found, one WARNING per sheet and no ERROR line and ended withSYSCC0, and with the folderDataof a test root it loaded 30, 16 and 5 rows. Not run in SAS: a drop or a promote that is refused, and the program as it ships, whose table names are not settings.- In a compute context whose session is another user than the one who started the job, the log holds one NOTE that says so. Measured in the second pass of 2026-10-12 in the
SAS Job Execution compute context: the NOTE, and then the file was read and the table loaded. One user written with a domain is one person; that comparison is not run in SAS. releaseTablenames a release table only: a table of that name that lacks one of the ten columns every release table has - all butbuilder_part, which the table of 2.2.0 lacks - is left as it is, and the job ends with an ERROR line andSYSCC1012. Measured on 2026-10-13 with a table made fromsashelp.class: the ERROR line, the table unchanged (19 rows, 5 columns, the same time stamps), no_NEWtable left,SYSCC1012. That check looked at a table in memory only. Where no table of that name is in memory, the job now lists the files of the CAS library, loads<name>.sashdat- what a restart of the CAS server leaves of a saved table - as a table of its session, reads its columns and drops it. A file that lacks the columns or cannot be loaded is left as it is, with an ERROR line of its own; a saved release table is loaded again. Where CAS refuses to list the files, the job drops the upload again and loads no table, withERROR: Load-Accelerator-Tables: no table <caslib>.<table> is in memory, and CAS refused to list the files of the CAS library <caslib> (the ERROR lines above), so it is not known whether a file a table of that name was saved to is there. ...andSYSCC1012. Measured in the second pass of 2026-10-13 incasuser, which held files of other tables: with a table of 19 rows that was only its saved file and is no release table, the job loaded the file as_LAT_SAVED, wrote its ERROR line -no table casuser.<name> is in memory, and the file a table of that name was saved to in the CAS library casuser could not be loaded or lacks columns of a release table. The file was left as it is and nothing was loaded(the wording of that build; the line now saysthe upload of ACCELERATOR_RELEASES.csv was dropped again and no table <caslib>.<table> was loaded) - and the file had the size and the time of before; with a release table that was only its saved file, the job loaded the file the same way and then loaded, promoted and saved the table (781 rows). Whattable.fileInfoanswers for a CAS library without any file is not measured. Where CAS refuses another action the job goes by there - the question whether the upload, the release table or_LAT_SAVEDis there, the count of the rows of the upload, or the listing of the columns of a table - the job drops the upload again and loads no table, withERROR: Load-Accelerator-Tables: CAS refused an action the job goes by to decide whether the upload takes the place of the table <caslib>.<table> - the ERROR lines above. ...andSYSCC1012. Before, a refused listing of the columns gave the line of a table that is no release table. Not run in SAS.- The job writes an ERROR line and sets
SYSCCto 1012 when a parameter is refused, when the release table is left as it is with an ERROR line of its own, and when one of the four tables is not in the CAS library at its end or CAS does not say whether it is - a question CAS refused, or a PROC CAS step of the check that halted: the line names every table the check got no answer for. A check whose PROC CAS step halted ran in the ninth pass, on 2026-10-15, and its line named all four tables; a question CAS refused is not run in SAS.
mdb load-releases and mdb load-facts load the same tables from a checkout and stay the way to load them from a workstation.
The job ran on the test system on 2026-10-12 from a test content root, with caslib=casuser and a release table of another name, so that no table of the system was written:
- As shipped, in a folder without
Load-Fact-Sheets.sas: the NOTEthe release table ... was loaded with 707 rows, promoted and saved, the WARNING thatLoad-Fact-Sheets.saswas not found, then the ERROR line that names the three fact sheet tables as not in the CAS library,SYSCC1012 and a failed job. A second run replaced the table. Without the CSV in the folder the table was left as it was, with the WARNING.releaseTable=x;ywas refused with one ERROR line, and nothing was read or written. - With the three fact sheet names replaced by test names - three lines of the job code and fifteen of
Load-Fact-Sheets.sas, nothing else: completed, no WARNING,SYSCC0, the release table with 707 rows and the sheets with 30, 16 and 5 rows, promoted and saved. Withoutdecision_fact_sheet.csvin the folder: the WARNING for that sheet, and the two others loaded. The comparison with the table ofmdbabove is of the table of this run, which read a copy of the CSV with LF line ends; for the job as shipped, which read the deployed file, 707 rows and equal column lengths were read. - Every job log of that run begins with three ERROR lines from the start of the SAS session about a connection to CAS on port 5570, in front of the first line of a program.
In the second pass of 2026-10-12, with Load-Fact-Sheets.sas removed from a second test root and a release table of 734 rows: in the SAS Studio compute context the same lines as the first run as shipped, with 734 rows and no NOTE about the session; in the SAS Job Execution compute context the NOTE that the session does not run as the user who started the job, and then the same lines - the file was read, and the table loaded into the personal CAS library of the signed-in user. mdb load-releases loaded 734 rows as well, PROC COMPARE found no difference, and the formatted lengths differed as above.
Not run: the job with its own table names in Public.
Access to the content folder
The package carries no authorization rule and no share: who may read the folder is decided on the site. The rules mdb setup writes cover the model repository, not this folder.
mdb builders-import writes a file sas-viya-cli-content-commands.txt into the current directory (--out <dir> names another one, --no-files leaves it out): two sas-viya authorization create-rule commands that give the LLM Consumers group — the group the commands of mdb setup create — Read on the content folder and on what is in it, with the id of the folder filled in. The command runs neither. Review the file, replace the group if you use another, and run the two commands with the SAS Viya CLI.
On a site that upgrades, this is also what gives the people who used the RAG Builder access to the folder Prompt Builder: the report and the job definition SAS-LLM-Prompt-Builder-UI that serve the RAG Builder now live there.
What was measured, and what has not run
The commands of this section ran on SAS Viya 2026.09 in eleven passes, twice on 2026-10-12, three times on 2026-10-13, three times on 2026-10-14, twice on 2026-10-15 and once on 2026-10-16, each with the commands as they were then, in test content roots below the folder of a user. The content folder of the system was only read, and no pass imported a package through the transfer service with mdb as it is now.
The first pass, and what it changed.
mdb content-deploy: the dry run, the run, a second run that found everything equal and wrote nothing, the update in place that keeps the id of a custom step and of a job definition, and objects outside the manifest that were left alone - see The content folder: deploy, export, check. A file that differed was deleted and uploaded again and had a new id; it is written in place since.mdb package-exportandmdb package-checkon exports of the test root: an export without the reports was refused, one with copies of the two reports passed the check, and every change made by hand was named.- A package of a folder that is not at the root of SAS Content landed in a new folder at the root.
mdb builders-importof that day handed the whole package to the transfer service. The package had been exported from the test root, held no report, and was imported on the system it came from. Afterwards a new folder with the name of the test root existed at the root of SAS Content, and the five sub-folders of the test root, with everything in them, had it as their parent; the command had saidimported 71 object(s).,Done.and ended with exit code 0. A copy of the package that was rewritten for a second test root arrived in a second new folder at the root, and the folder it was meant for stayed empty; the mapping the service returned for it held a version and nothing to name a folder with. This was seen for one test root, on the system the package was exported from, and no export that keeps the folders above its root was imported for comparison. - A file of the same name under another id was not replaced. In that import one file was in its folder under another id than the package names. The file in the folder kept its content and its id. The file of the package existed afterwards under the id of the package in no folder; its task said
was updated but cannot be added to the intended folderandAn item named "tokens.py" of type "File" already exists in the folder "rag_core". Three of the 71 tasks endedcompletedWithWarnings, the job saidcompletedwitherrorCount0, and the command showed none of it. What was on the system under the ids of the package was written in place: 18 custom steps, 35 files and three job definitions kept their ids. - Since then the command hands the transfer service only the reports and writes every file, custom step and job definition in place under the id it has on the site; it imports a report only into the folder the package was exported from, at the root of SAS Content; and it reads the tasks of the import job.
- Start Here opened without a compute context ran under a service account and checked nothing - see Start Here. Both job definitions carry
_contextNamesince. - A sign-in the system refused ended
mdb content-deploywith a traceback; the same command ran a minute and a half later.mdbasks such a refusal again since. Build-RAG-Cost-View.saswith two run tables failed and still said that its table was built - see The Third Run.
The second pass, with what the first had changed. Two new test content roots; a request log of every command; no request to the transfer service but the export side of mdb package-export; listings before and after, which found nothing created outside the two roots.
- The deploy in place.
72 entries: 70 created, 2 not deployed., then70 equalwithout a write. A file changed by hand:updated ... (content replaced in place; id <its id> kept)- onePUT /files/files/{id}/contentwithIf-Match, answered 200, no DELETE, the id and the creation time of the file kept, its bytes those of the checkout; the file and its content answered with the same ETag. A custom step and a job definition changed by hand kept their ids; a_contextNameset by hand survived a deploy;--compute-contextand--content-rootwrote theirs. - The export and the two forms of the gate. Without the reports: exit code 1 and four lines, and with
--allow-differencesa file of 71 objects; the lenient gate printed the parameters of the two job definitions asdrift:and the missing reports as errors,--releaseall four as errors; a file changed by hand in the root was adrift:line in the lenient form and an error with--release. The export deleted its package (HTTP 204) and left the record of its export job on the server; it deletes both since. - The import in place. A package exported from the first root, with one file changed by hand, imported with
--content-root <second root> --no-reports --no-releases --no-filesinto a root that held that file, a custom step and a job definition of the same names under other ids and with other content, and a file of its own: the dry run sent 10 GET requests and wrote nothing; the run said70 entries: 3 equal, 3 updated, 64 created., the threeupdatedlines with the ids of the root, sent no DELETE and no request to the transfer service, and afterwards the three had their ids and creation times and the content of the package - the file its 76 bytes, not the 1,020 of the checkout -,Start Herekept the_contextNameof the root and got the root ascontentRoot, and the file of the root's own was untouched. A second import was allequalwith GET requests only. - Two custom steps of one name in one folder could not be set up. SAS Viya refused the second registration with HTTP 409, errorCode 11552:
An item named "LLM - Get Options.step" of type "Custom step" already exists in the folder. Whatmdbdoes with two objects at one path was therefore not measured: a second step of one name could not be registered into the folder, and the state would need an object created outside the folder and added to it afterwards, which the pass did not do. - One part, somewhere else.
--towith a path updated a step in place (one PUT, id kept),--towith its id saidequal, a--tothat names nothing ended withNothing was updated.and exit code 1, and--only RAG --to <folder>created the 41 entries directly in that folder. - Start Here and Load-Accelerator-Tables in both compute contexts, the cost view with one and with two run tables, and LLM - Create LLM Call 1.1 - see Start Here, The tables of the reports and The Third Run. With an
options.jsonthat holds avalueslist, version 1.1 of the step failed in PROC TRANSPOSE; version 1.2 reads the options another way. - No sign-in was refused: the request logs hold 108 requests to the token address, each answered 200.
The third pass, on 2026-10-13, with what was built after the second. Two new test content roots below one new folder of a user, and test tables in casuser; a request log of every command and a guard on what it writes; no request to the transfer service but the export side of mdb package-export; listings before and after, which were equal. The content folder: deploy, export, check has the lines of the deploy, the export and the gate.
- A new root, and
release.jsonlast. Without--createthe command named the missing folder and wrote nothing; with it72 entries: 70 created, 2 not deployed., andrelease.jsonwas the last line of an object and the last write. - A stop leaves
release.jsonas it was. With a reference in the place of a file the entry wasFAILEDandrelease.jsonnot attempted, with GET requests only and exit code 1, and the file kept its time stamp and its bytes. A file that another writer changed between the read and the write of the command was refused with HTTP 412 under the ETag of the compared read: the run stopped there, 30 entries werenot attempted, and the next run updated the file in place. --to. The id of a step of another name was refused with the name and the folder of that step in the line -GET /folders/ancestorsanswered 200 -,--forceupdated it with its id kept, a path saidequal, and a folder that was not there was created only with--create.- The export. The DELETE of the export package and of the record of the export job were each answered 204, and the counts of both on the server were as before. All 143
createdByand 143modifiedByof the file were neutral; the 12userNameof its folder bodies were not, andmdb package-checkfailed the file for them in both forms. The commit check ran on copies of the file with another commit or date inrelease.json: no line for the commit of the checkout and for an ancestor, and a line each for a commit on another line of history, for one the clone does not hold, for a text that is no commit and for a wrong date. deploy-rag-content.ps1 -ContentRoot <folder> -DryRun -Insecure, with every stream redirected into a file: the file held the warning of the connection that is not verified and41 entries: 41 equal., and the request log GET requests only. The exit code of the script was not saved.- The import in place. A copy of the export with an empty folder and a folder named
..added, imported with--content-root <second root> --no-reports --no-releases --no-files --options <file>into a root whoseStart Hereheld a compute context set by hand: the dry run sent GET requests only; the run saidnot imported: folder '..' (...), created the empty folder, leftStart Herewith its compute context and the root ascontentRoot, gave the new job definitionLoad-Accelerator-Tablesthe ingestion compute context of the options, looked for the report at its place in the root (GET /folders/folders/@item?path=, no request to/reports/reports) and saidPrompt Builder: no such report in <root>/Prompt Builder - skipped., wroterelease.jsonlast and ended with exit code 0. With--compute-contextthe job definitionStart Herewasupdated ... (parameters; id <its id> kept). A second import was70 entries: 70 equalwithout a write. - A reference in the import. With a file of the root deleted and a reference of its name in its place the import said
FAILEDfor the entry, leftrelease.jsonnot attemptedand ended with exit code 1; the command it named for the repeat lacked the--no-filesof the run. With the file and a reference of its name both in the folder it said70 entries: 70 equal, listed the reference as left alone and ended with exit code 0. Both are changed since then: a reference of the name of an entry fails the entry, and the repeat is the run's own command. mdb options-save --content-root <root> --reports-onlyfor a root without a report:Nothing captured., exit code 1, four GET requests and none to/reports/reports.- The release table of the package.
mdb builders-import <file> --content-root <root> --no-reports --no-files --caslib casuser --table <a test name>saidRelease table: casuser.<table> loaded from Data/ACCELERATOR_RELEASES.csv of <file> (755 rows, latest dated release 2.2.0; 2.3.0 is in it as unreleased).; the file has 756 lines. - The job programs: LLM - Create LLM Call 1.2 and LLM - Get Options 1.1 (The content folder), Start Here in both compute contexts and with another address of the guides, the job Load-Accelerator-Tables with a
releaseTablethat names another table, the swap ofLoad-Fact-Sheets.sas, and the cost view at its first refusal; and the collection script with the parser, the pull and the link - see The Pass of 2026-10-13.
The fourth pass, the second of 2026-10-13, with what was built after the third. Two new test content roots below one new folder of a user, and test tables in casuser; a request log of every mdb command and a guard on what it writes; mdb sent the transfer service only the requests of mdb package-export, and no import job was started. The content folder: deploy, export, check and the changelog of the release have the lines.
- A part that names
release.jsonended with its sentence and exit code 1 and created nothing, in the dry run and in the run. It signed in first and printed the header of a run in front of the sentence; it does neither since then. --only Data --to <root>/Datawrote<root>as the content root into the job definition of that folder, and a target that does not end with/Dataended with exit code 2 without a sign-in.- A reference of the name of a file - in its place, and beside it - failed the entry with
mdb content-deployand withmdb builders-import, each line naming what the folder holds under that name; a reference named like a folder of the manifest failed the folder and every entry in it, in the dry run as in the run. Start Here showed the entry asmissingfor the reference alone and asok | presentbeside the file; the page was changed for the second. - A refused write - HTTP 412, staged by changing the file between the read and the write of the command - showed the message, the remediation and
(errorCode 0)of the answer whole. The command a failed import named for the repeat was its own, and run as printed, once the cause was gone, it ended with exit code 0. - The export replaced the user in all 298
createdBy,modifiedByanduserNameof its file;--keep-user-ids, the gate and--fixdid what their lines say; the release file was refused for another content root with exit code 2; and arelease.jsonwith an abbreviated commit failed both forms of the gate. The command the export printed for a test folder would have loaded the release table of the site: it was not run, and it was changed. - The compute context of the options went to the job definition of the root that had no
_contextName, and the one that held another context kept it. - The job programs: LLM - Create LLM Call 1.3 and LLM - Get Options 1.2 (The content folder); Start Here for both roots in both compute contexts; the job Load-Accelerator-Tables for a root without
Logging and Monitoringand for a table that is only its saved file; a copy ofLoad-Fact-Sheets.saswith test table names; and the collection script withescape_llm_log, version 2.7 of the parser as a code file, the pull and the link - see The Second Pass of 2026-10-13. - No sign-in was refused: the request logs of the pass hold 124 requests to the token address, each answered 200.
The fifth pass, the third of 2026-10-13, with what was changed after the fourth. Two new test content roots below one new folder of a user, and test tables in casuser; a request log of every mdb command and a guard on what it writes; mdb sent the transfer service only the requests of one mdb package-export, and no import job was started. The changelog of the release has the lines.
- What ends before the sign-in ran on the workstation with every sign-in refused by the harness of the pass, and sent no request: a part that names
release.json(exit code 1, its sentence as the only line),--only Data --toa folder that does not end with/Data(exit code 2), and the release file as--outof an export of another content root (exit code 2). - A port: with
:443inSAS_VIYA_URLa dry run ofmdb content-deploysent the 73 requests of the run without it and printed the same lines but for the first, which shows the address as it was given. - A reference of another kind - named like a file, to a folder - left the entry
equal, was listed at the end as(reference to a folder - not the file that is listed under this name)and the run ended with exit code 0; Start Here saidok | presentfor that file. - The hint of a
FAILEDline for a reference alone, run as printed with the URI the line names: refused without--force, and with it the object the reference names was updated in place, with one write. - A refused write - HTTP 412, staged as in the fourth pass - was one line of the output and ended
(errorCode 0))), at a width of 2,000 columns. - The export and the command it printed for a test folder, which had no
--createthen: the export replaced the user in 298 fields, and its command, with the second root filled in, created the 70 entries of the package in place - 11 folders, 37 files, 18 custom steps and four job definitions - without a request to the transfer service; a dry run behind it found the 70equal. - The job programs: LLM - Create LLM Call 1.4 and LLM - Get Options 1.3 as they come and with a model table (The content folder); Start Here with a file and a reference of its name in one folder; the job Load-Accelerator-Tables and a copy of
Load-Fact-Sheets.saswith a table that has the name of a helper table; and one collection with the script that redacts in the C locale, parsed with and without the lines of the script - see The Third Pass of 2026-10-13. - No sign-in was refused: the request logs of the pass hold 97 requests to the token address of the system, each answered 200. A wrong password and three other refusals went to a stand-in on the workstation only.
The sixth pass, on 2026-10-14, with what was changed after the fifth. Two new test content roots below one new folder of a user, test tables in casuser, and a credential domain and a group made for the pass; a request log of every mdb command and a guard on what it writes; mdb sent the transfer service only the requests of two mdb package-export, and no import job was started. The changelog of the release has the lines.
- What ends before the sign-in, with the address of the test system set, sent no request:
--only "",--only /, an empty--content-rootfor each of the five commands that take it, an empty--domain,<host>:443without a scheme and a port that is no number - each with exit code 2 and its line.https://<host>:443and the host alone made the dry run they make without a port. - A value as it is: a folder named
zz [two], a content root that is not there, an identity with brackets and the reasons of a write and an export that the harness of the pass refused were each printed as text, on one line. - The hints of a
FAILEDline, run as printed: for a reference in the place of the folderData, a path that ends with/Datagave a dry run of 5 entries, allequal, and another folder exit code 2 before the sign-in; in--only RAG --to <folder>, the hint for a reference in the place of a file was refused without--forceand updated the object in place with it; the line for a reference namedrelease.jsonnamed no command. - The list at the end of a run used the words
(registered step),(job definition)and(reference to a ... - not the ... that is listed under this name), and(reference to a casTable)for a reference to a CAS table. - The export and the command it printed for a test folder, run as printed into a second root that was not there: the dry run named the folder it creates, the run said
70 entries: 70 created.without a request to the transfer service, a second run70 entries: 70 equal, and no id of the first root was in the second. - The credentials commands in the domain and for the group of the pass: see Managing Credentials.
- Two objects of one name could not be set up: SAS Viya refused with HTTP 409 a second file, a second folder, a step registered twice and a second
release.jsonin one folder, and a reference beside an object of its name and kind, in both orders. Where a case could be set up, Start Here and a dry run ofmdb content-deployjudged it the same way. - The job programs: Start Here for both roots and with references; the job Load-Accelerator-Tables with a table
_LAT_SAVEDof the session;Create-LLM-Call.sas(The content folder); and version 2.9 of the log parser with the pull and the link - see The Pass of 2026-10-14. - No sign-in was refused: the request logs of the pass hold 148 requests to the token address of the system, each answered 200. An answer of 500 and one that says
Unauthorized grant typecame from a stand-in on the workstation only.
The seventh pass, later on 2026-10-14, with what was changed after the sixth. Two new test content roots below one new folder of a user, test tables in casuser, and a credential domain and a group made for the pass; mdb sent the transfer service only the requests of one mdb package-export, and no import job was started.
- What ends before the sign-in, with the address of the test system set, sent no request:
mdb credentials-apply --manifest "",mdb content-deploy --content-root / --dry-run,mdb credentials-applywith a manifest of another domain and--domain agentic-ai-keys, andSAS_VIYA_URLset tohttps://https://<host>,https://u:x@<host>andhttps:// <host>- each with exit code 2 and its line; the second address was shown ashttps://***@<host>. - The whole content folder into a new test root:
mdb content-deploywith--createended with 72 entries - 70 created and the two reports not deployed - and exit code 0. A read of every object afterwards found each equal to the checkout, andrelease.jsonnamed the commit of the checkout. - The export and the command it printed for a test folder: the export deleted its own package and export job. The printed
mdb builders-import, run into a second new root, created the 70 objects there in place, equal to the package but for the content root of Start Here and Load-Accelerator-Tables, which names the second root; no id of the first root was in the second, and the first root was unchanged. - The credentials commands in the domain and for the group of the pass: see Managing Credentials.
- The Prompt Builder: the request it sends for a run of a setup after a reload,
GET /jobExecution/jobswith a filter on the state of the job, sent once as the page sends it, was answered 200; with a job launched by its job definition running, its item carriedjobRequest.jobDefinitionUriand the match of the page found it. The page itself was not opened in that pass. - The log parser, with the drop of a table an append loaded for itself: see The Pass of 2026-10-14.
The eighth pass, late on 2026-10-14, with what was changed after the seventh. Two new test content roots below one new folder of a user and test tables in casuser; no credential domain and no group were made, and no import job was started.
- The whole content folder into a new test root:
mdb content-deploywith--create, after its dry run, ended with 72 entries - 70 created and the two reports not deployed - and exit code 0, and every object was equal to the checkout. - The export of a test folder: without
--allow-differences, 4 difference lines - the parameters of Start Here and Load-Accelerator-Tables, and the two reports -, the line of the differences ending with the sentence that in a folder other than/SAS Agentic AI Acceleratorsuch a difference stays and that--allow-differenceswrites the file all the same, thenNothing was written: there is no <file>.and exit code 1. With--allow-differences: the file, a summary that names thecontentRootof the job definitions as left as it is, and exit code 0. Each run deleted its export job and its package. Themdb builders-importthat the export printed, run into a second new root after its dry run, created the 70 objects there in place, without a request to the transfer service; the second root equalled the package but for the content root of the two job definitions. - The job programs of the first root, with every action of CAS answered: the log parser, the pull and the link, a copy of
Load-Fact-Sheets.saswith test table names,Build-RAG-Cost-View.sas, and the job Load-Accelerator-Tables, which loaded a test release table of 860 rows and ended with the ERROR of its end check for the three fact sheet tables the test root lacked, as in the passes before. See The Pass of 2026-10-14, which also has whatGet-All-Prompts.sasdid with a CAS session that had ended. - On the workstation, without a request to SAS Viya:
mdb retireagainst an address where nothing listens and with a blank insideSAS_VIYA_URL, the audit script ofmodel-auditwith such a blank, and the refusal of an identity id with a blank at its start and of one with an ESC. The audit script and the rule for identity ids were changed again after the pass.
The ninth pass, on 2026-10-15, with what was changed after the eighth. Two new test content roots below one new folder of a user, test tables in casuser, and a credential domain and a group made for the pass and deleted after it; no import job was started. mdb and the audit script signed in with a token taken before each run, as SAS_VIYA_TOKEN.
- The sign-in with a token: in each of the 25 runs that reached the test system, the
HEADof the address was answered with 302, and the command went on to its first request. - The whole content folder into a new test root:
mdb content-deploywith--createended with 72 entries - 70 created and the two reports not deployed - and exit code 0. Its export gave the 4 difference lines and exit code 1, and with--allow-differencesthe file; themdb builders-importit printed, run into a second new root after its dry run, created the 70 objects there in place, and a dry run ofmdb content-deployfor the second root found the 70 equal. - The credentials commands, with ids of printable ASCII, in the domain and for the group of the pass:
credentials-add,credentials-applyafter its dry run,credentials-report,credentials-removeand the commands with--identity, each with exit code 0; after the removals the report found no credential. - The audit script of
model-audit, read-only: one row for each of the 16 models of the test system and the three that need attention. - The job programs of the first root: the parser in its three states, and
Get-All-Prompts.sas, its step, the link and Load-Accelerator-Tables with PROC CAS steps that halt: see The Pass of 2026-10-14. - On the workstation, without a request to SAS Viya:
mdb list,credentials-report,load-factsandoptions-savewith a token against an address where nothing listens, andmdb listwith a CLI login and with the password, each with its one line and exit code 2;mdb retirewith that line and exit code 0, and a secondmdb retirewithalready deprecated.; the refusal of the identity idsLLM<U+00A0>Consumers,G1<U+2800>andJörgbycredentials-add, and of a credential domain outside printable ASCII in a manifest bycredentials-apply --dry-run, each with exit code 2.
The tenth pass, late on 2026-10-15, with what was changed after the ninth. Two new test content roots below one new folder of a user; no CAS table, no credential domain and no group were made, no job program ran, and no import job was started.
- The whole content folder into a new test root:
mdb content-deploywith--create, after its dry run, ended with 72 entries - 70 created and the two reports not deployed. Every object was equal to the checkout, andrelease.jsonnamed the commit of the checkout. - The export and the import in place:
mdb package-export --allow-differenceswrote the file and deleted its export job and its package;mdb package-checknamed the two reports, which are in the list and not in the package. Themdb builders-importthe export printed, run into a second new root after its dry run, created the 70 objects there in place without an import job of the transfer service, equal to the package but for the content root of the two job definitions; no id of the first root was in the second. Afterwards a dry run ofmdb content-deployfound the 70 objects of each root equal. - The Prompt Builder, served by SAS Job Execution from the first root with the bundle the deploy wrote there, and opened without a write: each of its 321 requests was a
GET. A stand-in in the browser added throw-away LLMs to the listing of the models and answered theiroptions.jsonand the keys of the credential domain. LLMs namedauthorandmodewere shown disabled with the note of their name, in English and in German, and were in none of the lists. Anoptions.jsonwith an enum option whose values are numbers left the page built and that LLM disabled with the note on itsoptions.jsonthe page had then; the bundle before showed Could not load the Prompt Builder. Every LLM and decision model of the test system was usable, as with the bundle before.
The eleventh pass, on 2026-10-16, with what was changed after the tenth. Two new test content roots below one new folder of a user, test tables in casuser, and a credential domain and a group made for the pass and deleted after it; no import job was started.
- The whole content folder into a new test root:
mdb content-deploywith--create, after its dry run, ended with 72 entries - 70 created and the two reports not deployed. Every object was equal to the checkout, andrelease.jsonnamed the commit of the checkout. - The export and the import in place:
mdb package-export --allow-differenceswrote the file with its 4 difference lines;mdb package-checknamed the two reports, which are in the list and not in the package. Themdb builders-importthe export printed, run into a second new root after its dry run, created the 70 objects there in place, equal to the package but for the content root of the two job definitions; no id of the first root was in the second. Afterwards a dry run ofmdb content-deployfound the 70 objects of each root equal. - Start Here of each root, run through SAS Job Execution: 105 checks, 93 of them ok, 11 missing - the two reports, which the test roots do not hold, and tables of
Publicthat the test system held only saved or not at all - and 1 not checked, the options of the report; the root held the same objects before and after. - The credentials commands, in the domain and for the group of the pass and for the test account:
credentials-init,credentials-addfor the group and for the user,credentials-applyafter its dry run (4 entries stored for each),credentials-report(2 of 2 named identities hold a credential),credentials-removefor both,credentials-applyafter its dry run, andcredentials-report(2 of 2 identities markedstate: absenthold none), each with exit code 0. - The job programs of the first root, each in three states of the CAS sessions, and the steps LLM - Log Parser 2.12 and LLM - Get All Prompts 2.7: see The Pass of 2026-10-14.
Create-Optimization-Dataset.sasfailed where the optionSESSREFnamed a session that was ended while_optbuildwas connected. - The Prompt Builder of the first root - the bundle the deploy wrote there, given to the browser at an address of the test system and opened without a write; the address of its form in SAS Job Execution (
_action=form) answered the shell of the web application of SAS Job Execution 3.1.5 instead. A stand-in in the browser added throw-away LLMs and decision models to the listings and answered theiroptions.json. An LLM whoseoptions.jsonholds an enum whose values are numbers, an enum without values or whose default is not one of its values, a bool with the default1, anAPI_KEYwhose default is 5, or abase_urlwhose default is an object or null was shown disabled with the note The options.json of this model is not one this page can read, in English and in German, and was in none of the lists. One with a label and a description oftemperaturethat are no texts and anAPI_KEYwith a number as its label was usable, its temperature a number field; one with the temperaturelow, an enum of texts and a bool whose default is the texttruewas usable with a text field, a choice and a ticked box. A decision model with further options of any kind was usable, one whoseAPI_KEYdefault is 5 was disabled with that note. Every LLM and decision model of the test system was usable. - On the workstation:
mdb generate,validateandretireof a manifest with an option the rule refused - an enum without values or whose default is not one of its values, a bool whose default is1, anAPI_KEYwhose default is 5, a default of.nan- each printed its one line and ended with exit code 1 without changing a file.
Not run against SAS Viya: what was changed after the eleventh pass - in mdb the refusal by import of an options.json where what it reads of it breaks the rule of an option, the one line of validate and add for an option whose name holds a line break, the refusal by retire of a generated file edited by hand before it saves the manifest, and the refusal of an option name with a comma or a colon and of a text default or an enum value with a comma, all run on the workstation; in the job programs the option SESSREF that Create-Optimization-Dataset.sas sets to a connected _optbuild where the option names a session that was ended; and in the Prompt Builder the id of each option's control (optionControlId), the temperature 0 of a judge only where its temperature is a number, and the refusal of an option name with a comma or a colon and of a text default or an enum value with a comma (bundle da79bbf8…), driven against the mock of the harness only. Of what was changed after the tenth pass, all but what the eleventh pass measured: in mdb the rule of an option of options.json that register checks before it writes (run on the workstation with SAS Viya replaced by a stand-in), and the order of retire, which renders before it saves the manifest (run on the workstation); and in the Prompt Builder what a run sends of each option, and the polls of an optimization job and of an ingestion sent one at a time (oneAtATime). Of what was changed after the ninth pass, all but the job programs and the two steps, which the eleventh pass ran: in mdb the traceback of an exception that neither a command of mdb nor the audit script caught and the message of a usage error of click, both now printed without control characters, the text of each exception in that traceback on the one line that names it (a line break in an answer is a blank), the same one line for the text of a caught exception in every line of mdb that can show an answer, the refusal of an empty port in SAS_VIYA_URL, and the refusal of a value of a credentials manifest that YAML does not read as text (or as a list for only, as true or false for verbatim) and the quotes that credentials-init, and credentials-add without a manifest, put around a value of the starter manifest where YAML would read it otherwise; and in the Prompt Builder the read back of a launch by the name in its request and the line of a launch whose job is not among the running jobs, the guard on the window, also for a click on a node inside a control, the box Best Decision Model and every text of the manifest of a decision template that names it, the check of every JSON file the page reads from SAS Model Manager, field for field, a tracker whose entries are not rows of a run among them, the prompts and model names of a saved run drawn as text, the chairman list and a loaded run without an LLM that cannot be used, one Best Decision Model per decision run, a model named like a field every object has that a saved run keeps, and a model of a name the page reads as a field of a run, or __proto__, that is not offered, and the reason its note gives. Of what was changed after the eighth pass, all but what the ninth pass measured - in mdb the line where the HEAD of a sign-in with SAS_VIYA_TOKEN or the SAS Viya CLI login gets no answer, which went to an address on the workstation, the HEAD of a CLI login, the line of a certificate that fails, and the refusal of an identity id or a credential domain outside printable ASCII, which ends before a request; in the job programs what a PROC CAS step that halts does to the parser (LLM - Log Parser 2.11) and to Load-Fact-Sheets.sas, and the severity the Python of the link takes for an answer; and in the Prompt Builder every page of a listing, the read back of a write without an answer, the lines for a history, a documentation or a result that cannot be read and for a file that holds nothing the page can read, the delete of an optimization run, Launch ingestion that waits for every question, Try again after Create setup, an action whose button is off, and the box of a decision run that Manifest takes, now Best Decision Model. Of what was changed after the seventh pass, all but the lines of mdb package-export for a test folder, which the eighth pass measured - in mdb the one line of a credentials request that gets no answer, the comparison of the answer of a refused write, the default a prompt shows, the lines for a 200 without a readable token, for a redirect to a page that answers 200 and for an address where nothing answers, the refusal of every empty value, of a --manifest that names a folder, of --env-file, --keys-file and --identity-type with a manifest, of an identity named twice with --identity and of an id with a line break, the line of mdb retire where SAS Viya cannot be used, the refusal of an identity id with another control character, a character of format or a blank at an end; in the job programs what a listing of saved files that CAS refused does to the parser (version 2.10), the link and Load-Accelerator-Tables, and what another action CAS refused does to the link, Load-Accelerator-Tables, Load-Fact-Sheets.sas, Build-RAG-Cost-View.sas and Get-All-Prompts.sas (LLM - Get All Prompts 2.5); and in the Prompt Builder the buttons that are off while a write is out, Launch ingestion and Run test among them, the trackers replaced in one upload, a Delete project that stops part of the way, a read that fails and the message of a delete of an optimization run whose answer is lost. Of what was changed after the sixth pass: the control characters mdb takes out of a value - the seventh pass saw an ESC taken out, on the workstation and in a file - and the quotes around a value in its sentences, the refusal of an identity listed twice, the line for a redirect whose target answers an error and for a 200 without a token, and in the credentials commands the comment on the state: line and the count of a credential that could not be read in the report; and in the job programs the ERROR line of the parser for a table whose rows can not be counted. Of what was changed after the fifth pass: the line for another status of the token address and one sign-in for Unauthorized grant type, which the sixth pass sent to a stand-in on the workstation; the row of a credential that cannot be read, which needs a second sign-in; the commands a line names but for the report mdb credentials-apply names; and in Start Here two objects of one name and a report with a reference beside it. Of what was changed after the fourth pass: one sign-in for an answer that says Bad credentials and the line for a 401 that sasctl cannot read, which the fifth pass sent to a stand-in on the workstation and not to SAS Logon; the two commands for two packages of which one is refused its reports, and the repeat with --no-reports, because no pass imported a report; the answer of a refused request whole and on one line in the messages other than the line of a refused write; mdb package-check for an id that is no commit; and the step of the workflow for a tag, which has not run on GitHub. And of what was built before it: a folder that cannot be created stopping the run, the other site settings, a report that is found at its place in the content root - the test roots held none -, the delete of the uploaded import package, and a delete the export is refused with its exit code 3. Never measured: an import of a package that holds the reports, and with it the capture and the write-back of the options, the import mapping and the commands file - that needs a system whose Prompt Builder report may be written; an import of a package whose root folder is at the root of SAS Content, which is how every release so far was installed; what the transfer service does with a folder or a report of the same name under another id, and with the neutral createdBy / modifiedBy / userName of the release package.
Importing without mdb
mdb leaves everything to the transfer serviceMeasured on 2026-10-12 (see What was measured): the transfer service did not replace a file that was in the target folder under the same name with another id - it wrote the file of the package into no folder and reported its job as completed. An object has another id than the package names where it did not come from an import of a package of the same origin: created by mdb content-deploy, by the earlier deploy script, or by hand. After an import without mdb, read the tasks of the import job, and run mdb content-deploy --dry-run from a checkout of the release: it says of every file, custom step and job definition whether it equals the checkout.
The one file imports like any transfer package. What mdb builders-import does beside the import is then hand work: the options of the Prompt Builder report are the defaults of the package afterwards — save them first, see Preserving builder options across a report import —, the tables stay bound to Public, the release table and the fact sheets are loaded by the job Load-Accelerator-Tables, and the access rules are yours to write.
SAS Environment Manager. Import the JSON from SAS Environment Manager → Content → Import. Then correct the host in step 5: open the report and update the Data-Driven Content object's base URL to your SAS Viya server.
SAS Viya CLI. Use the transfer plugin of the sas-viya command-line interface. This assumes you have installed the CLI, created a connection profile, logged in and installed the plugin as described in Introduction — SAS Viya CLI Setup. In the commands below substitute https://viya.example.com with your own SAS Viya URL.
-
Point the package at your host. The package ships the Prompt Builder object pointing at the placeholder host
https://your-sas-viya-host. Replace it in the JSON so that the imported report loads the app from your server (the percent-encoded program path after the host is left untouched):# bash / Linux / macOSsed -i 's#https://your-sas-viya-host#https://viya.example.com#' SAS-Agentic-AI-Accelerator.json# PowerShell / Windows(Get-Content SAS-Agentic-AI-Accelerator.json) `-replace 'https://your-sas-viya-host','https://viya.example.com' |Set-Content SAS-Agentic-AI-Accelerator.json -
Upload the package — the command prints the new package's
id:sas-viya transfer packages upload --file SAS-Agentic-AI-Accelerator.json -
Import it by that id:
sas-viya transfer packages import --id <package-id>Or do both in one go, capturing the id (requires
jq):pkg=$(sas-viya --output json transfer packages upload \--file SAS-Agentic-AI-Accelerator.json | jq -r '.id')sas-viya --output text transfer packages import --id "$pkg"(On older
transferplugin versions the same commands aresas-viya transfer upload/sas-viya transfer import— they still work, marked deprecated.)
The placeholder lives in the package's substitutions block, which is what SAS's import machinery is designed to remap. Editing the string before upload (step 1 above) is the simplest fix. Alternatively, remap it at import time with a mapping file: pass --mapping mapping.json to the upload and the CLI writes a JSON file whose substitutions block holds the entry with the placeholder URL — edit that value to your host, then pass the same file to the import:
sas-viya transfer packages upload --file SAS-Agentic-AI-Accelerator.json --mapping mapping.json
# edit mapping.json: replace https://your-sas-viya-host in the substitutions block
sas-viya transfer packages import --id <package-id> --mapping mapping.json
(sas-viya transfer get-mapping --id <package-id> regenerates the file for an already-uploaded package.) If you do neither, the report still imports; you then correct the object's base URL in step 5.
After the import, continue at step 3 — the Job Execution definition and the report already exist under SAS Content > SAS Agentic AI Accelerator > Prompt Builder.
Packages exported before release 2.3.0 hold a report named RAG Builder and a job definition RAG-Builder-UI — the standalone RAG UI that is retired. Importing such a file through SAS Environment Manager or the CLI puts both back. mdb builders-import names such a package before it imports it; use the package of a current checkout.
Preserving builder options across a report import
Every option you set on the Prompt Builder object — repository and project IDs, the SCR endpoint, the credential domain, the three switches, and for RAG the content root, the ingestion compute context and which vector stores the deployment offers — is stored inside the Visual Analytics report, not alongside it. A transfer package carries those values too, so importing a newer version of the report replaces your settings with whatever the package was built against.
This does not fail loudly. The report still opens and the app still runs; it simply shows the package's defaults — RAG switched off, no project IDs. mdb builders-import reads the options before it imports and writes them back; for an import made any other way, save your options first.
mdb builders-import without --options does both steps below by itself and keeps what it read in builder-options-captured-<time>.json. The two commands are for a file to keep with your deployment records, to edit before an import, to carry to another deployment, or for a package that is imported without mdb (see Model Definition Builder for installing the CLI and its [viya] extra):
# BEFORE importing a new report package
mdb options-save --file builder-options.json
# AFTER the import
mdb options-restore --file builder-options.json --dry-run # check what would change
mdb options-restore --file builder-options.json # write it back
options-save discovers the Model Manager repository and the LLM and embedding projects exactly as mdb setup does, then overlays whatever your live Prompt Builder report already holds — so a deployment that has been tuned is captured as tuned, not as freshly bootstrapped. Keep the resulting file with your deployment records; it supersedes the llm-prompt-builder.json seed as the record of how this environment is configured.
options-restore writes back only the options named in the file. The newly imported report keeps its new layout, data items and objects, and regains your configuration. It also replaces the shipped placeholder host your-sas-viya-host - in the object's Data-Driven Content URL and in the viyaHost / SCREndpoint defaults - with SAS_VIYA_URL, so a report imported by hand needs no URL edit afterwards (mdb builders-import does both in one run).
Points worth knowing:
-
The report is matched by name (
Prompt Builder). If you have renamed the report in your deployment, rename it in the file to match, or the restore skips it. -
An option the report does not have is reported, not inserted. That usually means the option was renamed or removed in the newer version of the app — worth reading the message rather than ignoring it. When the missing option is one the current Prompt Builder report has, the report on the site is an older one: everything it can hold is still written, and the command ends with exit code 1 so that the gap is not taken for a finished restore — import the package of this release and restore again. It is also why the shipped report already holds every option, including the ones that only show while a switch is on.
-
A switch is saved as the word
trueorfalse, which is what its checkbox in the Options pane stores. A file saved with 2.2.x holds the same two words forenableOptimization, so it restores unchanged. -
sslVerifyis saved and restored like every other option, as the same two words. A file saved with 2.2.x does not name it, and nothing supplies a value for it: the imported report keeps its own default, which is ticked. A package is exported with the option ticked: on a report that would ship it switched off,mdb package-exportends with exit code 1 and writes nothing. -
API keys are deliberately not saved. This file is meant to be kept with deployment paperwork and reviewed in a diff, which is no place for a provider key. The Prompt Builder reads its keys from the credential domain at run time — see Managing Credentials.
-
The file records which deployment it came from. Restoring it against a different SAS Viya URL still works — moving configuration between environments is a legitimate thing to do — but the command says so first, because doing it by accident is exactly the failure this guards against.
-
Concurrent edits fail loudly. The write is conditional on the report being unchanged since it was read, so a colleague editing the report in Visual Analytics at that moment causes an error rather than silently losing their work.
-
A sign-in that is refused can pass. Seen on 2026-10-11 and again on 2026-10-12: a call of
mdbended with401 Client Errorat the sign-in and went through when it was repeated; the sign-in service of that system refused password grants that overlapped.mdbasks a refused sign-in again, up to three times (MDB_LOGIN_ATTEMPTSsets the number, and 1 switches the repeat off). A sign-in that is answeredBad credentialsis not asked again. Neither is one answeredUnauthorized grant type: the client may not use the password grant, whenever it is asked. Measured on 2026-10-13:/SASLogon/oauth/tokenanswers a password grant with a wrong password with status 401,Content-Type: application/json, the 64-byte body{"error":"invalid_client","error_description":"Bad credentials"}and the headerWWW-Authenticate: Basic realm="UAA/client", error="unauthorized", error_description="Bad credentials". So a wrong password costs one failed sign-in per command. A refusal that stays ends the command with one line and exit code 2, which says how many sign-ins were made and what SAS Logon answered:SAS Viya at <url> refused the sign-in of <user> with SAS_VIYA_USER + SAS_VIYA_PASSWORD (1 sign-in made): Authentication failed for user '<user>' - sasctl says so for an answer whose error_description says "Bad credentials". ...or, for any other 401,... (3 sign-ins made): HTTP 401 from /SASLogon/oauth/token with error "<error>" and error_description "<text>". .... That includes a 401 whose body sasctl cannot read as it expects - no JSON object, anerror_descriptionthat is no text,Unauthorized grant type-: the line then nameserroranderror_descriptionwhere the answer has them. Both are shown whole and as SAS Logon wrote them. Any other status of the token address - a 500, a 503, a 400 - is not read as a refusal and not asked again; the command ends with one line,SAS Viya at <url> did not answer the sign-in of <user> ... with a token: HTTP <status> from /SASLogon/oauth/token ..., and exit code 2. So do a redirect of the token address to a page that answers an error, or to one that answers 200 without a token, and a 200 that carries no token mdb can read - no JSON, noaccess_token, or one that is null, no text or empty. WithSAS_VIYA_USER+SAS_VIYA_PASSWORD, an address where nothing answers - a port nothing listens on, a certificate, a timeout - ends the command at the sign-in withSAS Viya at <url> could not be asked for the sign-in of <user> with SAS_VIYA_USER + SAS_VIYA_PASSWORD: <kind of exception> (<what requests says>). Check SAS_VIYA_URL and that SAS Viya can be reached from here.and exit code 2. WithSAS_VIYA_TOKENor the SAS Viya CLI login, the sign-in sends oneHEADof the address and does not read its answer. Where it gets no answer the command ends withSAS Viya at <url> could not be asked with the access token (SAS_VIYA_TOKEN): <kind of exception> (<what requests says>). Check SAS_VIYA_URL and that SAS Viya can be reached from here.-(the SAS Viya CLI login)for a login - and exit code 2. A certificate that fails ends the command with such a line, withSSLErrorin it, for every way in, the password grant too.mdb retire, which tags the definition without SAS Viya, prints the line of an address it cannot use or of a sign-in that failed afterThe registered model's status was not updated:and goes on, with exit code 0. A port inSAS_VIYA_URL(https://host:8443) is used.SAS_VIYA_URLis the address with its scheme,https://<host>orhttps://<host>:<port>:host:8443without a scheme, a port that is no number or empty (https://<host>:), a second//behind the scheme, a blank inside the address and a user or a password in front of the host (shown as***@) end the command with one line before anything is asked of SAS Viya; a host alone is taken as https. Blanks, tabs and line breaks at either end of the value are taken out. Not recorded: the body of the 401 that was seen while sign-ins overlapped. The output saved on 2026-10-12 shows that sasctl 1.11.8 passed that refusal on as theHTTPErrorof requests, which it does for a 401 whoseerror_descriptiondoes not sayBad credentials; a refusal like it is asked up to three times, as before. On 2026-10-13 twelve and then 24 correct password grants sent at the same moment were all answered 200 (2.6 to 3.5 s): the refusal did not come back, and the 124 sign-ins of the second pass of that day were each answered 200. In the third pass of that day a wrong password and three other refusals were sent to a stand-in on the workstation, not to SAS Logon - one sign-in and the line forBad credentials, and for the rest three sign-ins, or one withMDB_LOGIN_ATTEMPTS=1, and the other line, as the command was then -, and with:443inSAS_VIYA_URLa dry run sent the system the 73 requests of the run without it. The repeat, the single sign-in forBad credentialsand forUnauthorized grant type, the lines of a refusal, the line for another status, and the lines of a redirect, of a 200 without a token and of an address where nothing answers are not run against SAS Viya, and neither is theHEADof a sign-in with a token or a CLI login: the answer SAS Viya gives it was not measured. The refusal of an address that cannot be used was measured in the two passes of 2026-10-14 (What was measured).
Measured on 2026-10-11 on SAS Viya 2026.09 with the Prompt Builder report of this release, after the report had been saved once in SAS Visual Analytics with Enable RAG setups ticked: in a round of mdb options-save and mdb options-restore, the dry run of the restore named six options to write and none that the report lacks, and the restore wrote them.
On a brand-new environment with no live report to capture from, run mdb options-save anyway: with nothing to overlay it writes the discovered values alone, which is the same content the seed file carries.
The retired RAG Builder report
Up to release 2.2 the RAG Builder had a report of its own, named RAG Builder, with its own options. A site that upgrades still has it, and its options are carried into the Prompt Builder report by a fold that all three commands share:
mdb options-savereads the liveRAG Builderreport when one exists and writes onePrompt Buildersection.mdb builders-importwithout--optionsreads both reports the same way before it imports. Runmdb options-savebefore the import and before deleting the old report — afterwards the new report holds its own defaults, and what the Prompt Builder report holds always wins over the old report. A site that imported first still has a way back while the old report exists:options-savenames every value of the old report that it did not carry, with what the old report held. Copy the ones you want intobuilder-options.jsonand runmdb options-restore.mdb options-restoreandmdb builders-import --optionsfold a file saved with 2.2.x, which has aRAG Buildersection, in the same way before they write. Nothing is ever written to the retired report, and nothing is deleted.
What the fold does, and prints as it does it:
| In the old report | In the Prompt Builder section |
|---|---|
The RAG options (embeddingProjectID, contentRoot, casServer, storeSslmode, deletedPolicy, retainDays, recordHistory, embedReplicas, persistElements, persistChunks, enable_pgvector, enable_singlestore) | Carried under the same name with the stored value, where the Prompt Builder section has none |
computeContext | Becomes ragComputeContext. It is never written to the Prompt Builder's computeContext, which is the context of the optimization job |
viyaHost, modelRepositoryID, SCREndpoint, deploymentType, credentialDomain | The Prompt Builder value is kept; a different value in the old report is printed. If the credential domains differ, the RAG part now reads its secrets from the kept one — add the vector-store entries to it. If the kept value is none, there is no domain to add them to and RAG cannot be used: the message names the domain of the old report — set credentialDomain to it in the file before restoring, or in the Options pane afterwards. Models without an API key are called as before |
enabledBackends | Dropped — it has been unused since the per-store switches exist |
| (nothing) | enableRag is set to true when the site had the report RAG Builder — live, or as the RAG Builder section of a file saved from one — and only if the Prompt Builder section does not hold an enableRag yet. The sign is a content root or a CAS server in the old section, which the report shipped with 2.2.x holds from the start |
The old report does not record whether anyone used it, so the fold switches RAG on at every site that had it. A site that does not use RAG runs mdb options-save, sets "enableRag": "false" in the Prompt Builder section of builder-options.json and imports with mdb builders-import --options builder-options.json, or clears Enable RAG setups in the Options pane afterwards. A 2.2.x file that was saved while no RAG Builder report existed holds only discovered values in its RAG Builder section; enableRag is then not set and RAG stays off.
While a report named RAG Builder is still on the site, options-restore and builders-import say so on every run: it keeps serving the old standalone UI with its own options. Once the Prompt Builder report shows your RAG setups, delete the report RAG Builder and the job definition RAG-Builder-UI next to it (SAS Environment Manager → Content, or SAS Drive), and give the people who used it Read access to the Prompt Builder folder if they did not have it - see Access to the content folder. mdb deletes nothing; mdb content-deploy and the in-place part of mdb builders-import name the two objects - RAG/RAG Builder (report) and RAG/RAG-Builder-UI (job definition) - among what is in the folder and not in the package. Afterwards bookmarks to that report and to the URL of RAG-Builder-UI are dead. A package that was exported before 2.3.0 still holds both objects, and importing one brings them back.
1. Get the single-file app
You have two options:
Option A — use the prebuilt file (recommended). Download the ready-to-use dist/index.html from the repository. No Node.js or build step is required — skip straight to step 2. Or copy the code from there to your clipboard and move to step 2.
Option B — build it yourself. With Node.js (^20.19 || >=22.12) installed:
git clone https://github.com/sassoftware/sas-agentic-ai-accelerator.git
cd sas-agentic-ai-accelerator/LLM-Prompt-Builder
npm install
npm run build
This produces the same single-file dist/index.html, with all three kinds in it — there is no separate build for the RAG Builder. See the project README for local development and customization details.
2. Create a SAS Job Execution definition
The single-file app is served to Visual Analytics through SAS Job Execution:
- In SAS Data and AI Studio, click on New > Job definition to create a new Job Definition
- In the Job Definition (edit) tab go to the right hand side bar and click on the first icon (Associate a Form) and change it from Prompt designer to HTML form.
- Copy the full contents of
dist/index.htmland paste it into the Job Definition (edit) tab - Save the job to the place you want it in SAS Content - note that people that should be using it need read access.
- On the right hand side bar click the second icon (Properties), expand the Details section and copy the value under Job URL - it should look something like this:
https://your-viya-host/SASJobExecution/SASJobExecution/?_program=path-to-your-job-definitionafter the path value add the following:&_action=form, as this enables us to view the HTML form without having to provide any SAS code. Save that URL as we will need it in fifth step.
SAS Job Execution serves HTML through a Go template engine that treats {{ … }} as directives. The build base64-encodes every inline <script> and decodes it at runtime so the minified bundle — which inevitably contains {{/}} — is not corrupted. This is automatic and requires no action.
3. Provide the API keys via the credential domain
The Prompt Builder resolves provider API keys from a SAS Viya credential domain (default agentic-ai-keys) under the identity of the signed-in user — a user credential overrides a group credential, and models the user holds no key entry for are shown disabled with a note. RAG setups take the vector-store credentials, and where each store lives, from the same domain. Set the domain up once with the CLI-based scripts as described in Managing Credentials; no data source, table, or report assignment is involved.
Keys live encrypted in the Credentials service — never in the report definition, the URL, or a data table. Who holds a credential in the domain (directly or via group membership) is who can run paid model calls.
4. SAS Environment Manager configuration
To let the app run inside SAS Visual Analytics, set the following Content Security Policy directives via SAS Environment Manager → Configuration → View Definitions.
SAS Visual Analytics — sas.commons.web.security → SAS Visual Analytics → content-security-policy:
default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src * blob: data:; frame-src * blob: data: mailto:; connect-src 'self' *.sas.com login.microsoftonline.com graph.microsoft.com *.arcgis.com *.arcgisonline.com; object-src 'none'
SAS Job Execution — sas.commons.web.security → SAS Job Execution → content-security-policy (replace <sas-viya-host> with your environment's URL). The 'unsafe-inline' in script-src is what lets the single-file page run: its small loader unpacks the gzipped script and adds it as an inline script. The Prompt Builder needs no 'unsafe-eval' (releases up to 2.4.0 did); keep it only where another page served by SAS Job Execution on your site needs it:
default-src 'self'; script-src 'self' 'unsafe-inline' https://<sas-viya-host>; style-src 'self' 'unsafe-inline'; img-src * blob: data:; child-src 'self' blob: data: ; frame-ancestors 'self'; form-action 'self';
IFrame sandbox — verify the sas.visualanalytics definition's IFrame Sandbox Attribute Value contains at minimum allow-same-origin allow-scripts.
That sandbox does not include allow-popups, which is why the links that leave the page — "Open in SAS Model Manager", the link to a saved prompt-test, the links to a decision in the delete dialogs — copy their URL instead of forcing a new tab on a normal click (use the browser's right-click → Open link in new tab). Add allow-popups to the IFrame Sandbox Attribute Value to let these links open a tab directly. A link is copied only where no tab opened: with allow-popups a click opens the tab and leaves the clipboard alone.
The page carries the referrer policy same-origin, a meta element in its head: what it asks of another origin than its own carries no Referer, what it asks of its own origin keeps it. A link to another site — the user guide link of the Optimize step — therefore does not tell that site the address of your SAS Viya host. A tab the page opens on SAS Viya itself, such as SAS Model Manager, is told where the click came from, as before, and the calls to SAS Viya keep their Referer; that holds where the page is served from the SAS Viya origin, as in the setup described on this page, and not where the viyaHost parameter names another origin. A call to a model container on another host carries no Referer either, but that host still learns where the page runs: a browser names the origin of the page in the Origin header of every cross-origin call, whatever the referrer policy.
Restart the following kubernetes pods of your SAS Viya environment if saving the changes didn't trigger a restart automatically:
- sas-job-execution
- sas-job-execution-app
- sas-logon-app
- sas-visual-analytics
- sas-visual-analytics-app
Example kubectl command:
kubectl get pods -n <your-namespace> -o name | grep -E 'pod/sas-job-execution|pod/sas-logon-app|pod/sas-visual-analytics' | xargs kubectl delete -n <your-namespace>
5. Add the object to a Visual Analytics report
If you imported the transfer package then open up the SAS Visual Analytics report under SAS Content > SAS Agentic AI Accelerator > Prompt Builder > Prompt Builder and then continue.
- In a Visual Analytics report, add a Data-Driven Content object and in the Options pane under Web Content enter the URL from the previous step or if you imported it update the base URL to your SAS Viya server.
- Assign the release table to the object: in the Data pane pick
Public.ACCELERATOR_RELEASES(loaded bymdb setupormdb load-releases; the library and name followSAS_CAS_LIBRARY/SAS_RELEASES_TABLE) and add any of its columns to the object's role. A Data-Driven Content object does not render until it has a data assignment; the Prompt Builder itself reads nothing from it, and provider keys still come from the credential domain (step 3). The table carries the accelerator's changelog - one row per release and per change, with acomponentcolumn - so the same report can show what changed for the Prompt Builder next to the object. A change to the Prompt Builder is one row for each part of it that it concerns, named in the columnbuilder_part(Prompt,Decision,RAG): a page that lists the changes keeps its filter oncomponentand gets a button bar onbuilder_part, with which a reader picks the part - see The release table for the rows and for what a page built before the column shows. - Open the object's Properties panel and set the configuration values (see below).
Configuration (Properties panel)
The object publishes one list of options, in the order of the tables below: first the options every kind shares, then the three switches, each directly followed by the options that only matter while it is on. Visual Analytics writes every value into the object's URL under the URL parameter name and reloads the app, which is how a changed switch makes its options appear or disappear.
The five environment-specific values are exactly those captured in your llm-prompt-builder.json — the file produced in the Setup SAS Model Manager chapter (regenerate it by re-running mdb setup if you have lost it), where each stands under the name of its URL parameter: modelRepositoryID, llmProjectID, embeddingProjectID, SCREndpoint and deploymentType. mdb builders-import writes the same five into the report by itself, so copying them by hand is only needed for a report you built yourself.
Shared by every kind — always shown:
| Properties-panel field | URL parameter | Default | Meaning |
|---|---|---|---|
| SAS Viya host | viyaHost | the embedding origin | SAS Viya base URL. Leave blank to default to the embedding origin. |
| Model Manager repository ID | modelRepositoryID | blank | Model Manager repository in which new prompt projects, decision projects and RAG projects are created. Required by every kind. |
| LLM project ID | llmProjectID | blank | Model Manager project holding the available LLM definitions (each with an options.json). Required for prompt and decision templates; a page that only builds RAG setups can leave it blank. |
| SCR endpoint | SCREndpoint | blank | Base URL of the SCR endpoint hosting the LLM and decision model containers and, for RAG, the embedding containers. Required for prompt and decision templates; RAG reads a blank value as <SAS Viya host>/llm, the host being the SAS Viya host option or the address the page is opened at. For a RAG setup the one resolved value goes to the ingestion job, Test retrieval, the Embed step of the generated SAS Studio flow and the retrieval model, so it has to be reachable from the compute session of the ingestion and from the publishing destination — set the option where the address of the page is not. |
| Deployment type | deploymentType | k8s | k8s or aca (Azure Container Apps / Instances) — how the model containers are deployed. See Container Deployment. |
| Default judge model | judgeModel | blank | Optional. Name of an LLM in the LLM project used by default for the LLM-as-a-Judge comparison; prompt engineers can still override it in the app. Leave blank for no default. |
| Model card report URI | modelCardReportURI | blank | Optional. A SAS Visual Analytics report path (/reports/reports/<uuid>). When set, manifesting the best prompt embeds that report on its model card as the custom chart, hosted from the SAS Viya host above. Leave blank to omit. |
| Credential domain | credentialDomain | agentic-ai-keys | Credential domain that provider API keys and, for RAG, the vector-store credentials resolve from (see step 3). Enter none when only key-less self-hosted models are used: no credential is then looked up for prompt and decision templates. RAG needs a domain — with none its card is shown disabled with that reason. The SAS code of an LLM call that LLM - Create LLM Call, LLM - Get Options and Create-LLM-Call.sas write does not read this option: it names agentic-ai-keys in its line %let API_KEY_DOMAIN=agentic-ai-keys;, which a site with another domain edits. |
| Verify TLS certificates in manifested models | sslVerify | ticked | A checkbox. Ticked: a model manifested in the Prompt Builder verifies the certificate of the model container it calls. Not ticked: models manifested from then on do not verify it, carry the tag TLS-Verification-Off in SAS Model Manager, and the step that manifests them shows a warning. See below. |
Verify TLS certificates in manifested models
Three kinds of manifested model make an HTTPS call themselves when they are scored: a prompt template manifested with Include the LLM call in the manifested model, a decision template, and the retrieval model of a RAG setup. Each verifies the certificate of what it calls against the CA bundle of the SAS Viya deployment. sslVerify decides what is written into the score code of such a model when it is manifested:
- It is a default written at manifest time. Changing the option changes the models manifested from then on. A model that is already manifested keeps what it was manifested with, in SAS Model Manager and on every destination it is published to, until it is manifested again.
- It changes nothing else. The calls the Prompt Builder page makes in the browser, the ingestion job of a RAG setup, and a prompt manifested for the Call LLM node (where the node makes the call) are as before.
- The SAS Studio custom step has a checkbox of its own. A retrieval model registered with RAG - Register Setup is not manifested on this page, and the step does not read
sslVerify. Its checkbox Verify TLS certificates in the registered retrieval model (Runtime section, ticked by default) writes the same line into the same score code and sets and removes the same tag — see RAG Ingestion and Retrieval. - A destination can overrule it in both directions with an environment variable:
LLMCONTAINERSSLVERIFYfor prompt and decision templates,RAGEMBED_SSLVERIFYandRAGSTORE_SSLVERIFYfor the retrieval model.trueswitches verification on for a model manifested with it off,falsethe reverse — see Deployment of Decisions. - Off is visible. A model manifested while the option is not ticked carries the tag
TLS-Verification-Off, on all three kinds, and manifesting it again with the option ticked removes the tag. The step shows a warning before the click that names this option and the variable that turns verification back on: Finalize for a decision template, Finalize for a prompt only while Include the LLM call in the manifested model is ticked, and Manifest setup for a RAG setup. Finding the models that do not verify lists such models and names the line to look for in a score code. - A report saved before the option existed shows the checkbox ticked, and so verifies. The state of the checkbox is stored with the report: it is there again after the report is saved and reopened.
Leave it ticked unless a destination cannot verify. The case it exists for is SAS Micro Analytic Service running Python in a container that does not have the CA bundle; Publishing to SAS Micro Analytic Service: the certificate bundle describes it, the fix that keeps verification on, and when switching it off is acceptable — a test system on a closed cluster network, not production.
In the URL the option reads like a switch: Visual Analytics writes sslVerify=true or sslVerify=false; in a URL written by hand 0, no and off also switch verification off, in any case. A blank, missing or any other value keeps it on.
The three switches — always shown, each a checkbox:
| Properties-panel field | URL parameter | Default | Ticked | Not ticked |
|---|---|---|---|---|
| Enable decision templates | enableDecision | ticked | The Setup card offers decision templates for decision models, with the question designer, the Evaluate step and the typed manifest. | The kind is not offered and nothing is shown for it. The Decision Model Project is not looked up and no decision model is called. Existing decision projects and templates are not listed; they stay untouched in SAS Model Manager. |
| Enable prompt optimization (DSPy) | enableOptimization | not ticked | Prompt templates gain the Optimize step, and the three Optimization: options below appear. See Enabling Prompt Optimization. | No Optimize step, and no request to SAS Job Execution or to the folder of the optimize job. |
| Enable RAG setups | enableRag | not ticked | The Setup card offers the RAG setup, and the RAG: options below appear. Nothing is requested for RAG until a user chooses that kind — the RAG Builder is loaded on first use. | The kind is not offered; nothing of the RAG Builder is loaded and no RAG project, embedding project, CAS library or content folder is requested. Stored RAG setups stay untouched. |
A switch that is off costs nothing at run time, which is the point of having it: a site that does not publish decision models, or has no vector store, should neither show the kind nor make requests that can only fail. With one kind left, the question What are you building? is not shown at all.
The report stores a switch as the word true or false. Up to release 2.2 Enable prompt optimization (DSPy) was a dropdown with Enabled and Disabled that stored the same two words, so a report saved then keeps its setting: a stored true shows as a ticked checkbox.
Only while prompt optimization is enabled:
| Properties-panel field | URL parameter | Default | Meaning |
|---|---|---|---|
| Optimization: compute context | computeContext | blank | SAS Compute context the optimization job runs in. It must run as the requesting user — SAS Studio compute context does, the stock SAS Job Execution compute context does not, and the job ends there before its first model call. Its Python needs dspy. Not used by RAG. |
| Optimization: job path | optimizeJobProgram | blank | SAS Content path of the optimize job definition, e.g. /Public/Jobs/Optimize-Prompt-DSPy. |
| Optimization: minimum samples | minOptimizeSamples | 30 | Minimum dataset rows before an optimization run is allowed; the Optimize panel warns below 50. |
Only while RAG is enabled — what each one governs is explained in RAG Ingestion and Retrieval:
| Properties-panel field | URL parameter | Default | Meaning |
|---|---|---|---|
| RAG: embedding model project ID | embeddingProjectID | blank | Model Manager project holding the registered embedding models. The RAG Builder lists that project instead of asking for a model name; without it no embedding model can be chosen and no setup can be saved. |
| RAG: content root | contentRoot | /SAS Agentic AI Accelerator/RAG | SAS Content folder that holds the RAG runtime - the folder RAG of the content folder, which the transfer package brings. Generated job definitions go to its generated subfolder unless a setup names another. |
| RAG: CAS server | casServer | cas-shared-default | CAS server of the ingestion ledger and the pipeline tables. |
| RAG: ingestion compute context | ragComputeContext | blank | SAS Compute context the ingestion job and the retrieval test run in. It must run as the requesting user — SAS Studio compute context does, the stock SAS Job Execution compute context does not. Launching and testing are refused until it is set. The folder picker of the Documents step lists the folders of the compute server in a session of this context, as the user. |
| RAG: start folder on the compute server | ragServerFolder | blank | The folder of the compute server the folder picker of a RAG setup opens in. Blank: it opens at /, and every folder the user may read can be chosen. |
| RAG: allow folders above the compute server start folder | ragServerBrowseAbove | not ticked | Not ticked: with a start folder set, the picker cannot go above it and a setup can only be saved with a folder below it. Ticked: the picker opens in the start folder and can go up. A guide for the users of the page, not a permission: what the file system lets the user read decides, and the ingestion job reads the folder its definition names. |
| RAG: start folder in SAS Content | ragContentFolder | blank | The same for a setup whose documents are in SAS Content. Blank: the picker opens at the top folders of SAS Content. |
| RAG: allow folders above the SAS Content start folder | ragContentBrowseAbove | not ticked | The same for SAS Content. |
| RAG: vector store TLS | storeSslmode | prefer | How the ingestion connects to the vector store: prefer, require, verify-ca, verify-full, allow or disable. Stores other than PostgreSQL read disable as no TLS and anything else as TLS on. |
| RAG: when a document disappears from the source | deletedPolicy | retire | retire keeps its chunks as history that no retrieval returns; purge removes them for good. |
| RAG: keep retired chunks for (days) | retainDays | 0 | Retired chunk generations older than this are dropped after each load. 0 keeps them forever. |
| RAG: record run history | recordHistory | on | A checkbox. Writes rag_runs, rag_doc_events and rag_configs beside the collection and publishes the first two to CAS. |
| RAG: embedding container replicas | embedReplicas | 1 | How many replicas of the embedding container the deployment runs; the ingestion sizes its parallel calls from it. |
| RAG: save the element table to disk | persistElements | 1 | 1 saves <prefix>_ELEMENTS to disk, 0 keeps it in memory only. The value is recorded on each setup; the SAS Studio flow that Manifest setup generates does not apply it yet and keeps this table in memory either way. |
| RAG: save the chunk table to disk | persistChunks | 1 | The same for <prefix>_CHUNKS; in the generated flow it reaches the enrichment step only so far. The ledger and the embedded chunks are always saved. |
| RAG: offer pgvector (PostgreSQL) | enable_pgvector | on | A checkbox per vector database: whether users may choose it for a new setup. A setup that already uses a withheld database keeps it and says so. |
| RAG: offer SingleStore | enable_singlestore | on | As above. |
| RAG: offer Weaviate | enable_weaviate | on | As above. |
computeContext is the context of the optimization job and ragComputeContext the context of the RAG ingestion; neither is ever read for the other. Both have to run as the requesting user: the stock SAS Job Execution compute context runs its servers as a service account, where an ingestion cannot reuse its CAS session (see the two prerequisites) and the optimization job ends before its first model call (see Enabling Prompt Optimization). They are two options because a site may give each job a context of its own, with the Python packages that job needs. Before release 2.3.0 the RAG Builder's own report called its context computeContext too; mdb options-save renames it when it carries that report's options over.
Until the object is configured it shows a "Configuration required" message and does not call SAS Viya, so it never fails against placeholder IDs. The message names what is missing for each side, and completing either is enough for the page to load: prompt and decision templates need the repository ID, the LLM project ID and the SCR endpoint; RAG setups need the repository ID, Enable RAG setups ticked and a credential domain other than none. A site with no LLM project therefore gets the RAG Builder alone.
Every field can also be appended to the object's URL by hand using its URL parameter name above (for example &judgeModel=...&modelCardReportURI=/reports/reports/<uuid>), which is a reliable fallback if your Visual Analytics version does not render the options panel. For a switch, Visual Analytics writes true for a ticked checkbox and false for one that is not; in a URL written by hand a switch also reads 1, yes or on as on and 0, no or off as off. Anything else, and a blank or missing value, is its default.