Skip to main content
Technical ManualLooking for step-by-step instructions? In the User Manual: Estimator Assistant

Estimator AI Module

The CrestApps.OrchardCore.Estimator.AI module connects CrestApps Estimator to the CrestApps AI suite. It gives the suite five read-only Estimator tools, a structured extraction service that returns typed values that code checks, and a record of the AI usage of each project. The AI connections, deployments, profiles, chat and usage records are those of CrestApps AI; the module adds no AI provider of its own.

Feature IDNameCategoryDependencies
CrestApps.OrchardCore.Estimator.AIEstimator AIEstimatorCrestApps.OrchardCore.Estimator.Catalog, CrestApps.OrchardCore.Estimator.Estimates, CrestApps.OrchardCore.AI

The design is recorded in docs/engineering/adr/0017-ai-plumbing.md in the repository.

The Estimator AI assistant recipe​

The EstimatorAIAssistant recipe (Tools > Recipes > Estimator AI assistant) is not a setup recipe. Run it on a site that has an AI connection and deployments (see Configuration). It:

  • enables CrestApps.OrchardCore.AI, CrestApps.OrchardCore.OpenAI.Azure, CrestApps.OrchardCore.AI.Chat and the Estimator AI feature;
  • turns on AI usage tracking (GeneralAISettings.EnableAIUsageTracking), which the usage of each project needs;
  • adds two AI profiles that use the default deployments of the site.
ProfileTypeTools and agentsNotes
estimator-assistant (Estimator Assistant)Chatestimator_get_estimate_summary, estimator_get_estimate_lines, estimator_explain_line, estimator_extract_panel_schedule; the estimator-catalog-specialist agentOn the admin menu. Temperature 0.2, the last 10 messages. The title of a session is the first prompt (TitleType: InitialPrompt), because a generated title is written without the tools and can show invented numbers
estimator-catalog-specialist (Catalog Specialist)Agent, on demandestimator_search_catalogAgentMetadata.AllowToolInvocation is true. Without it, AgentProxyTool runs an agent as one completion with its tools off, and the agent answers without the catalog

The system messages tell the models to take every number from a tool and never to invent prices, quantities or totals. The recipe step updates a profile with the same name, so running the recipe again restores the shipped profiles. An administrator can add the Estimator tools to other profiles in the Capabilities tab of the profile, under Estimator.

Tools​

The tools are AIFunction singletons registered with AddCoreAITool<T>(name) and .Selectable(), in the category Estimator. Each tool resolves its services per call from AIFunctionArguments.Services, reads the caller from CrestApps.Core.Security.IUserAccessor, and checks the same permissions as the admin. No tool changes data.

ToolArgumentsReturnsNeeds
estimator_search_catalogquery, limit (1 to 25, default 10)Items with today's price and its basis and the normal labor unit, and assemblies, with the number that matchViewEstimatorCatalog
estimator_get_estimate_summaryestimateIdThe project, the current revision and its state, the line count, material, labor hours, labor, the selling price, the recap rows and the alternatesThe project is visible to the caller
estimator_get_estimate_linesestimateId, search, offset, limit (1 to 100, default 50)The calculated lines of the current revision, with the total that matchThe project is visible to the caller
estimator_explain_lineestimateId, lineIdThe calculation of one line from the engine's trace: each item with its installed and purchase quantities, material, labor hours and labor; the steps of the engine (prices, waste, labor units, labor factors); the labor rate; and the items missing from the catalogThe project is visible to the caller
estimator_extract_panel_scheduletext (at most 20,000 characters), projectId (optional)The typed panel schedule and the problems code foundThe caller may use the projects; the usage is charged to projectId only when the caller may see that project

The catalog search matches every word of the query, in any order and in any field (item number, description, manufacturer, catalog number; code and name of an assembly). It asks the catalog store for the longest word and checks the other words on at most 500 candidates. Single letters, such as the "A" of "20 A", are ignored.

An estimate of a project the caller may not see is answered as not found, as the API does. Without a caller nothing is read. An error is returned to the model as { "error": "..." }, so the model can say it.

Structured extraction​

IStructuredExtractionService.ExtractAsync<T>(StructuredExtractionRequest) asks a model for a value of type T:

  • the deployment is AIDeploymentSlotNames.Utility resolved with IAIDeploymentManager.ResolveSlotAsync, or the deployment the request names;
  • the call is IChatClient.GetResponseAsync<T> with useJsonSchemaResponseFormat: true and temperature 0. The deployment must declare structuredOutputs; otherwise CrestApps removes the response format and the model answers without the schema;
  • the request is labeled with AIUsageScope (context type Estimator, the request's purpose or DataExtraction) and with the project and estimate of the request;
  • every failure is a message in StructuredExtractionResult<T>.Error: no text, no deployment, an answer not in the schema, or a provider error (logged as a warning). Only cancellation is thrown.

PanelScheduleExtractor uses it with the purpose PanelScheduleExtraction and then checks the schedule in code. Normalize trims the texts and sorts the circuits. Validate returns these problem codes:

CodeMeaning
NameMissingThe panel has no name
InvalidPhasesThe phases are not 1 or 3
InvalidCircuitNumber:nA circuit number below 1
InvalidPoles:nA breaker with other than 1, 2 or 3 poles
DuplicateCircuit:nTwo circuits with the same number
OverlappingCircuit:nA circuit taken by the poles of another breaker. A breaker of p poles on circuit n takes n, n + 2 and so on, the circuits on the same side of the panel

The model reads; code decides what is wrong. To add another extraction, write a typed model, instructions and checks, and call IStructuredExtractionService the same way.

Title blocks​

When the code reader leaves the sheet number or the title of a vector sheet empty, TitleBlockSheetHandler (an IDrawingSheetEventHandler) queues a SheetTitleBlock processing job with the SHA-256 of the sheet's TitleBlockText as its content hash (pipeline version 1), and the project's bid priority. When the same text was read before, the job comes from the cache and the earlier result is applied at once, without an AI request.

TitleBlockJobs (the handler) reads the text with TitleBlockExtractor (structured extraction, purpose TitleBlockExtraction) and Apply fills the empty fields. A sheet without a number that the code reader suggested also takes the model's title, because the code reader guesses the title from the sheet number. A sheet a person checked (IsSuggested false) keeps its title. Sheet numbers are written the usual way (E 601 becomes E-601), and the discipline comes from the number. A failed extraction is an error to retry.

AI usage of projects​

CrestApps meters every request to the provider and sends each record to the IAICompletionUsageObservers. EstimatorAIUsageObserver keeps an EstimatorAIUsageRecord (project, estimate, purpose, operation, deployment, model, tokens, user, date) for the requests made inside an EstimatorAIUsage.Begin(projectId, estimateId) scope, in the Estimator collection with EstimatorAIUsageIndex. IEstimatorAIUsageStore.ListByProjectAsync reads them. Requests outside such a scope, such as chat turns, are only in the CrestApps usage records. Both need AI usage tracking on.

Tests​

tests/CrestApps.OrchardCore.Estimator.Tests/AI holds:

  • EstimatorToolsTests: each tool on real SQLite with the test catalog: visibility by project, the engine's numbers, the word search, and usage records in a project scope;
  • StructuredExtractionTests: the extraction with a fake chat client (schema, temperature, labels, every failure) and the panel schedule checks;
  • TitleBlockJobsTests: which sheets get a job, the fields filled, the guessed title replaced, a person's title kept, the cache, and a failed extraction;
  • AIRecipeTests: the recipe enables the features, selects only tools that exist, lets the agent run its tool, and titles sessions with the first prompt.