Documentation Index
Fetch the complete documentation index at: https://docs.artosai.com/llms.txt
Use this file to discover all available pages before exploring further.
npm SDK Examples
Get Template URL for a Document
import { getTemplateUrl } from '@artosai/sdk'
async function fetchTemplateInfo(documentId) {
try {
const result = await getTemplateUrl({
documentId: documentId,
accessToken: process.env.ARTOS_ACCESS_TOKEN,
baseUrl: 'https://api.artosai.com'
})
if (result.templateUrl) {
console.log('Template found!')
console.log(' URL:', result.templateUrl)
console.log(' Template ID:', result.templateId)
console.log(' Document Name:', result.documentName)
} else {
console.log('No template associated with this document')
}
return result
} catch (error) {
console.error('Failed to fetch template:', error.message)
throw error
}
}
fetchTemplateInfo('507f1f77bcf86cd799439011')
Display a Template in a Web Application
import { getTemplateUrl } from '@artosai/sdk'
async function displayTemplate(documentId) {
const result = await getTemplateUrl({
documentId,
accessToken: process.env.ARTOS_ACCESS_TOKEN,
baseUrl: 'https://api.artosai.com'
})
if (result.templateUrl) {
// Use the presigned URL directly in an iframe, download link, or document viewer
document.getElementById('template-viewer').src = result.templateUrl
// Or create a download link
const link = document.createElement('a')
link.href = result.templateUrl
link.download = result.documentName || 'template.docx'
link.click()
}
}
Using the API Client for Multiple Requests
import { createApiClient, getTemplateUrl } from '@artosai/sdk'
const artosClient = createApiClient({
baseUrl: 'https://api.artosai.com'
})
async function getMultipleTemplates(documentIds, accessToken) {
const results = await Promise.all(
documentIds.map(id => getTemplateUrl({
documentId: id,
accessToken,
apiClient: artosClient
}))
)
return results
}
const templates = await getMultipleTemplates(
['doc-1', 'doc-2', 'doc-3'],
process.env.ARTOS_ACCESS_TOKEN
)
React Integration
import React, { useState, useEffect } from 'react'
import { getTemplateUrl } from '@artosai/sdk'
function TemplateViewer({ documentId, accessToken }) {
const [templateUrl, setTemplateUrl] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
async function loadTemplate() {
try {
setLoading(true)
const result = await getTemplateUrl({
documentId,
accessToken,
baseUrl: process.env.REACT_APP_ARTOS_API_URL
})
setTemplateUrl(result.templateUrl)
} catch (err) {
setError(err.message)
} finally {
setLoading(false)
}
}
loadTemplate()
}, [documentId, accessToken])
if (loading) return <div>Loading template...</div>
if (error) return <div>Error: {error}</div>
if (!templateUrl) return <div>No template found</div>
return (
<iframe
src={templateUrl}
title="Template Viewer"
width="100%"
height="600px"
frameBorder="0"
/>
)
}
export default TemplateViewer
Node.js/Express Backend Integration
import express from 'express'
import { getTemplateUrl, ArtosAPIError } from '@artosai/sdk'
const app = express()
const getAccessToken = (req) => {
return req.headers.authorization?.replace('Bearer ', '')
}
app.get('/api/documents/:id/template', async (req, res) => {
try {
const result = await getTemplateUrl({
documentId: req.params.id,
accessToken: getAccessToken(req),
baseUrl: process.env.ARTOS_API_URL
})
res.json({
success: true,
data: {
templateUrl: result.templateUrl,
templateId: result.templateId,
documentName: result.documentName
}
})
} catch (error) {
const status = error instanceof ArtosAPIError ? error.status : 500
res.status(status).json({
success: false,
error: error.message
})
}
})
app.listen(3000, () => {
console.log('Server running on port 3000')
})
Get Sources for a Document Section
import { getDocumentSources } from '@artosai/sdk'
async function fetchSources(documentId, sectionId) {
try {
const result = await getDocumentSources({
documentId,
sectionId,
accessToken: process.env.ARTOS_ACCESS_TOKEN,
baseUrl: 'https://api.artosai.com'
})
console.log(`Found ${result.sources.length} sources:`)
result.sources.forEach(source => {
console.log(` - ${source.documentSourceName}`)
console.log(` Text: ${source.referencedText?.substring(0, 100)}...`)
})
return result.sources
} catch (error) {
console.error('Failed to fetch sources:', error.message)
throw error
}
}
Full Source Viewing Workflow
This example shows the complete flow: user highlights text, the SDK resolves the section, fetches sources, and generates a preview URL.import {
getSectionFromText,
getDocumentSources,
getDocumentSourceUrl
} from '@artosai/sdk'
async function handleTextSelection(documentId, selectedText) {
const accessToken = process.env.ARTOS_ACCESS_TOKEN
const baseUrl = 'https://api.artosai.com'
// Step 1: Resolve the section from highlighted text
const section = await getSectionFromText({
documentId,
highlightedText: selectedText,
accessToken,
baseUrl
})
if (!section.sectionId) {
console.log('No matching section found for the selected text')
return null
}
// Step 2: Get all sources for that section
const { sources } = await getDocumentSources({
documentId,
sectionId: section.sectionId,
accessToken,
baseUrl
})
// Step 3: Get presigned URLs for each source
const sourcesWithUrls = await Promise.all(
sources.map(async (source) => {
const { documentSourceUrl } = await getDocumentSourceUrl({
documentSourceId: source.documentSourceId,
accessToken,
baseUrl
})
return { ...source, url: documentSourceUrl }
})
)
return sourcesWithUrls
}
const sources = await handleTextSelection(
'doc-123',
'The competitive landscape has shifted significantly...'
)
List All Sections in a Document
import { getDocumentSections } from '@artosai/sdk'
async function listSections(documentId) {
try {
const result = await getDocumentSections({
documentId,
accessToken: process.env.ARTOS_ACCESS_TOKEN,
baseUrl: 'https://api.artosai.com'
})
console.log(`Found ${result.sections.length} sections:`)
result.sections.forEach(section => {
console.log(` ${section.sectionOrder}. ${section.sectionTitle} (${section.sectionId})`)
})
return result.sections
} catch (error) {
console.error('Failed to fetch sections:', error.message)
throw error
}
}
Get a Document’s Presigned Download URL
import { getDocumentPresignedUrl } from '@artosai/sdk'
async function downloadDocument(documentId) {
try {
const result = await getDocumentPresignedUrl({
documentId,
accessToken: process.env.ARTOS_ACCESS_TOKEN,
baseUrl: 'https://api.artosai.com'
})
if (result.documentUrl) {
console.log('Download URL:', result.documentUrl)
// Trigger a browser download
const link = document.createElement('a')
link.href = result.documentUrl
link.download = ''
link.click()
} else {
console.log('No file available for this document')
}
return result
} catch (error) {
console.error('Failed to get document URL:', error.message)
throw error
}
}
Stream a Source File Through the Proxy
import { getDocumentSourceUrl, generateProxyUrl } from '@artosai/sdk'
async function previewSourceFile(documentSourceId) {
const baseUrl = 'https://api.artosai.com'
const accessToken = process.env.ARTOS_ACCESS_TOKEN
// Step 1: Get the presigned URL for the source
const { documentSourceUrl } = await getDocumentSourceUrl({
documentSourceId,
accessToken,
baseUrl
})
if (!documentSourceUrl) {
console.log('No URL available for this source')
return null
}
// Step 2: Stream the file through the proxy
const { blob, contentType } = await generateProxyUrl({
presignedUrl: documentSourceUrl,
baseUrl
})
console.log(`Fetched ${contentType} file (${blob.size} bytes)`)
// Display in the browser
const objectUrl = URL.createObjectURL(blob)
window.open(objectUrl, '_blank')
return { blob, contentType }
}
React Source Panel Component
import React, { useState, useCallback } from 'react'
import {
getSectionFromText,
getDocumentSources,
getDocumentSourceUrl
} from '@artosai/sdk'
function SourcePanel({ documentId, accessToken }) {
const [sources, setSources] = useState([])
const [loading, setLoading] = useState(false)
const onTextSelected = useCallback(async (selectedText) => {
setLoading(true)
try {
const { sectionId } = await getSectionFromText({
documentId,
highlightedText: selectedText,
accessToken,
baseUrl: process.env.REACT_APP_ARTOS_API_URL
})
if (!sectionId) {
setSources([])
return
}
const { sources } = await getDocumentSources({
documentId,
sectionId,
accessToken,
baseUrl: process.env.REACT_APP_ARTOS_API_URL
})
setSources(sources)
} catch (err) {
console.error('Error loading sources:', err.message)
} finally {
setLoading(false)
}
}, [documentId, accessToken])
const openSource = async (documentSourceId) => {
const { documentSourceUrl } = await getDocumentSourceUrl({
documentSourceId,
accessToken,
baseUrl: process.env.REACT_APP_ARTOS_API_URL
})
if (documentSourceUrl) {
window.open(documentSourceUrl, '_blank')
}
}
if (loading) return <div>Loading sources...</div>
return (
<div className="source-panel">
<h3>Sources ({sources.length})</h3>
{sources.map(source => (
<div key={source.documentSourceId} className="source-item">
<strong>{source.documentSourceName}</strong>
<p>{source.referencedText}</p>
<button onClick={() => openSource(source.documentSourceId)}>
View Original
</button>
</div>
))}
</div>
)
}
export default SourcePanel
Get Section Rules
Fetch all rules for a section with their source data, breadcrumbs, template URLs, and metadata.import { getSectionRules } from '@artosai/sdk'
async function fetchRules(sectionId) {
const result = await getSectionRules({
sectionId,
accessToken: process.env.ARTOS_ACCESS_TOKEN,
baseUrl: 'https://api.artosai.com'
})
console.log(`Found ${result.rules.length} rules:`)
result.rules.forEach((rule, i) => {
console.log(` Rule ${i + 1}: ${rule.ruleType} (confidence: ${rule.confidenceScore})`)
// Template URL is only present for template-related rules
if (rule.templateUrl) {
console.log(` Template: ${rule.templateUrl}`)
}
// Breadcrumbs trace the full generation pipeline
if (rule.breadcrumbs) {
const stages = Object.keys(rule.breadcrumbs.stages || {})
console.log(` Breadcrumb stages: ${stages.join(', ')}`)
}
// Rule metadata contains structured template/instruction data
if (rule.ruleMetadata) {
console.log(` Metadata keys: ${Object.keys(rule.ruleMetadata).join(', ')}`)
}
})
return result.rules
}
React Rules Inspector Panel
A full-featured rules panel that displays generated content, source references, template links, and the processing audit trail.import React, { useState, useEffect } from 'react'
import { getSectionRules, generateProxyUrl } from '@artosai/sdk'
function RulesInspector({ sectionId, accessToken }) {
const [rules, setRules] = useState([])
const [loading, setLoading] = useState(true)
const [expandedRule, setExpandedRule] = useState(null)
useEffect(() => {
async function loadRules() {
try {
setLoading(true)
const result = await getSectionRules({
sectionId,
accessToken,
baseUrl: process.env.REACT_APP_ARTOS_API_URL
})
setRules(result.rules)
} catch (err) {
console.error('Failed to load rules:', err.message)
} finally {
setLoading(false)
}
}
if (sectionId) loadRules()
}, [sectionId, accessToken])
if (loading) return <div>Loading rules...</div>
if (!rules.length) return <div>No rules found for this section.</div>
return (
<div className="rules-inspector">
{rules.map((rule, index) => (
<div key={rule.ruleId} className="rule-card">
{/* Rule header */}
<div
className="rule-header"
onClick={() => setExpandedRule(expandedRule === index ? null : index)}
>
<span className="rule-order">#{rule.orderIndex + 1}</span>
<span className="rule-type-badge">{rule.ruleType}</span>
<span className="rule-confidence">
{rule.confidenceScore != null
? `${(rule.confidenceScore * 100).toFixed(0)}%`
: 'N/A'}
</span>
</div>
{/* Generated content preview */}
<p className="rule-content">
{rule.generatedContent?.substring(0, 200)}
{rule.generatedContent?.length > 200 ? '...' : ''}
</p>
{/* Template link (only for template rules) */}
{rule.templateUrl && (
<a
href={rule.templateUrl}
target="_blank"
rel="noopener noreferrer"
className="template-link"
>
View Original Template
</a>
)}
{/* Expanded detail view */}
{expandedRule === index && (
<div className="rule-details">
{/* Source chunks */}
<SourceChunksList rule={rule} />
{/* Table data */}
{rule.table && <TablePreview table={rule.table} />}
{/* Breadcrumbs audit trail */}
{rule.breadcrumbs && (
<BreadcrumbsTrail breadcrumbs={rule.breadcrumbs} />
)}
{/* Rule metadata */}
{rule.ruleMetadata && (
<RuleMetadataPanel metadata={rule.ruleMetadata} />
)}
</div>
)}
</div>
))}
</div>
)
}
function SourceChunksList({ rule }) {
// Single source chunk (copy_paste, summarize_content)
if (rule.sourceChunk) {
return (
<div className="source-section">
<h4>Source</h4>
<SourceChunkCard chunk={rule.sourceChunk} />
</div>
)
}
// Multiple source chunks (template_text, template_table)
if (rule.sourceChunks?.length > 0) {
return (
<div className="source-section">
<h4>Sources ({rule.sourceChunks.length})</h4>
{rule.sourceChunks.map(chunk => (
<SourceChunkCard key={chunk.chunkId} chunk={chunk} />
))}
</div>
)
}
return null
}
function SourceChunkCard({ chunk }) {
return (
<div className="source-chunk-card">
<div className="chunk-header">
<strong>{chunk.sectionName || 'Source Document'}</strong>
{chunk.pageNumber && <span className="page-badge">Page {chunk.pageNumber}</span>}
</div>
<p className="chunk-content">{chunk.content?.substring(0, 300)}...</p>
{chunk.presignedUrl && (
<a href={chunk.presignedUrl} target="_blank" rel="noopener noreferrer">
View Source Document
</a>
)}
</div>
)
}
function TablePreview({ table }) {
return (
<div className="table-preview">
<h4>{table.tableTitle || 'Table'}</h4>
{table.tableMarkdown && (
<pre className="table-markdown">{table.tableMarkdown}</pre>
)}
{table.presignedUrl && (
<a href={table.presignedUrl} target="_blank" rel="noopener noreferrer">
View Source Table
</a>
)}
</div>
)
}
export default RulesInspector
Displaying Rule Breadcrumbs (Audit Trail)
Breadcrumbs trace the full lifecycle of each rule through the generation pipeline. Each rule passes through up to four stages, and the breadcrumbs record what happened at each one. This is valuable for audit, compliance, and transparency use cases.Breadcrumbs Structure Overview
breadcrumbs
├── schema_version // Always 2
├── created_at // ISO8601 timestamp
├── updated_at // ISO8601 timestamp
├── trace // Immutable rule lineage
│ ├── rule_trace_id // Unique ID (e.g. "rb_a3f8c1...")
│ ├── rule_type // The rule type
│ ├── section_title // Parent section name
│ ├── blueprint_rule_id // Link to template rule (if any)
│ └── source_chunk_ids // IDs of source chunks used
├── stages // Per-stage processing data
│ ├── generation // Initial content generation
│ ├── execution_pre_postprocessing // Rule execution trace
│ ├── postprocessing // Content cleanup & flow pass
│ └── style_guide_text // Style guide application pass
└── trace_display // Pre-computed display-friendly summary
├── decision_notes // { completed, incomplete, missing }
└── discrepancies // Array of flagged issues
The Four Breadcrumb Stages
1.generation - Records how the rule was selected and why. Contains the search evidence, selection rationale, missing content acknowledgments, and expansion reasoning.
2. execution_pre_postprocessing - Records the rule execution trace: status, duration, iteration counts, step-level details, input/output sizes, and any errors or missing data notes.
3. postprocessing - Records what changed during the content cleanup pass. Includes whether content was modified, domain-level rationales (e.g., “Updated to align with document flow”), and change attribution.
4. style_guide_text - Records the style guide application pass. Includes per-domain rationales (e.g., which style guide rules were applied), references to specific style guide sections, and whether content was changed.
React Breadcrumbs Component
function BreadcrumbsTrail({ breadcrumbs }) {
if (!breadcrumbs) return null
const stages = breadcrumbs.stages || {}
const traceDisplay = breadcrumbs.trace_display || {}
const stageLabels = {
generation: 'Generation',
execution_pre_postprocessing: 'Execution',
postprocessing: 'Post-Processing',
style_guide_text: 'Style Guide'
}
const stageOrder = [
'generation',
'execution_pre_postprocessing',
'postprocessing',
'style_guide_text'
]
return (
<div className="breadcrumbs-trail">
<h4>Processing Audit Trail</h4>
{/* Quick summary from trace_display */}
{traceDisplay.decision_notes && (
<DecisionNotesSummary notes={traceDisplay.decision_notes} />
)}
{/* Discrepancies / warnings */}
{traceDisplay.discrepancies?.length > 0 && (
<div className="discrepancies-panel">
<h5>Flagged Issues</h5>
{traceDisplay.discrepancies.map((d, i) => (
<div key={i} className={`discrepancy ${d.category}`}>
<strong>{d.title}</strong>
{d.description?.map((desc, j) => <p key={j}>{desc}</p>)}
</div>
))}
</div>
)}
{/* Stage-by-stage breakdown */}
{stageOrder.map(stageKey => {
const stage = stages[stageKey]
if (!stage) return null
return (
<details key={stageKey} className="stage-accordion">
<summary>
<span className="stage-label">{stageLabels[stageKey]}</span>
<StageStatusBadge stage={stage} stageKey={stageKey} />
</summary>
<StageDetail stageKey={stageKey} stage={stage} />
</details>
)
})}
</div>
)
}
function DecisionNotesSummary({ notes }) {
return (
<div className="decision-notes">
{notes.completed?.length > 0 && (
<div className="notes-completed">
<h5>Completed</h5>
<ul>{notes.completed.map((n, i) => <li key={i}>{n}</li>)}</ul>
</div>
)}
{notes.incomplete?.length > 0 && (
<div className="notes-incomplete">
<h5>Incomplete</h5>
<ul>{notes.incomplete.map((n, i) => <li key={i}>{n}</li>)}</ul>
</div>
)}
{notes.missing?.length > 0 && (
<div className="notes-missing">
<h5>Missing</h5>
<ul>{notes.missing.map((n, i) => <li key={i}>{n}</li>)}</ul>
</div>
)}
</div>
)
}
function StageStatusBadge({ stage, stageKey }) {
if (stageKey === 'generation') return null
// Postprocessing and style_guide stages have a 'changed' boolean
if (stage.changed === true) return <span className="badge changed">Changed</span>
if (stage.changed === false) return <span className="badge unchanged">Unchanged</span>
// Execution stage has status
if (stage.execution_trace?.status === 'success') {
return <span className="badge success">Success</span>
}
if (stage.execution_trace?.status === 'error') {
return <span className="badge error">Error</span>
}
return null
}
function StageDetail({ stageKey, stage }) {
switch (stageKey) {
case 'generation':
return <GenerationStageDetail stage={stage} />
case 'execution_pre_postprocessing':
return <ExecutionStageDetail stage={stage} />
case 'postprocessing':
case 'style_guide_text':
return <DomainStageDetail stage={stage} />
default:
return <pre>{JSON.stringify(stage, null, 2)}</pre>
}
}
function GenerationStageDetail({ stage }) {
const rationale = stage.rule_decision_notes?.rationale
const whyWhyNot = stage.rule_decision_notes?.why_why_not
const searchNotes = stage.selection?.search_final_answer?.notes
const whyGenerated = stage.why_generated?.rule_generation_final_answer
return (
<div className="stage-detail generation">
{rationale && (
<div className="field">
<label>Selection Rationale</label>
<p>{rationale}</p>
</div>
)}
{whyWhyNot && (
<div className="field">
<label>Why Selected / Why Not Others</label>
<p>{whyWhyNot}</p>
</div>
)}
{searchNotes && (
<div className="field">
<label>Search Evidence</label>
<p>{searchNotes}</p>
</div>
)}
{whyGenerated && (
<div className="field">
<label>Generation Evidence</label>
{whyGenerated.source_notes && <p>{whyGenerated.source_notes}</p>}
{whyGenerated.missing_content_notes && (
<p className="warning">{whyGenerated.missing_content_notes}</p>
)}
{whyGenerated.expansion_rationale && (
<p>{whyGenerated.expansion_rationale}</p>
)}
</div>
)}
</div>
)
}
function ExecutionStageDetail({ stage }) {
const trace = stage.execution_trace
if (!trace) return <p>No execution trace available.</p>
return (
<div className="stage-detail execution">
<div className="execution-summary">
<span>Status: <strong>{trace.status}</strong></span>
{trace.duration_ms && <span>Duration: {trace.duration_ms}ms</span>}
{trace.iteration_count && <span>Iterations: {trace.iteration_count}</span>}
{trace.input_chars && <span>Input: {trace.input_chars} chars</span>}
{trace.output_chars && <span>Output: {trace.output_chars} chars</span>}
</div>
{trace.summary && <p className="trace-summary">{trace.summary}</p>}
{trace.error && <p className="error-message">{trace.error}</p>}
{trace.why_not_notes?.length > 0 && (
<div className="why-not-notes">
<label>Why-Not Notes</label>
<ul>{trace.why_not_notes.map((n, i) => <li key={i}>{n}</li>)}</ul>
</div>
)}
{trace.missing_data_notes?.length > 0 && (
<div className="missing-data-notes">
<label>Missing Data</label>
<ul>{trace.missing_data_notes.map((n, i) => <li key={i}>{n}</li>)}</ul>
</div>
)}
{trace.steps?.length > 0 && (
<div className="execution-steps">
<label>Execution Steps ({trace.step_count})</label>
{trace.steps.map((step, i) => (
<div key={i} className={`step ${step.status}`}>
<strong>{step.step_label || step.step_key}</strong>
<span className="step-status">{step.status}</span>
{step.duration_ms && <span>{step.duration_ms}ms</span>}
{step.summary && <p>{step.summary}</p>}
</div>
))}
</div>
)}
</div>
)
}
function DomainStageDetail({ stage }) {
const changed = stage.changed
const domains = stage.domain_rationales || []
const attribution = stage.change_attribution
return (
<div className="stage-detail domain">
<p>Content {changed ? 'was modified' : 'was not modified'} during this stage.</p>
{attribution?.summary && (
<div className="change-summary">
<label>Change Summary</label>
<p>{attribution.summary}</p>
</div>
)}
{domains.length > 0 && (
<div className="domain-rationales">
<label>Domain Decisions ({domains.length})</label>
{domains.map((domain, i) => (
<div key={i} className={`domain-item ${domain.status}`}>
<strong>{domain.domain_id}</strong>
<span className={`badge ${domain.status}`}>{domain.status}</span>
{domain.rationale && <p>{domain.rationale}</p>}
{domain.why_not && (
<p className="why-not">{domain.why_not}</p>
)}
{domain.style_guide_refs?.length > 0 && (
<div className="style-refs">
Refs: {domain.style_guide_refs.join(', ')}
</div>
)}
</div>
))}
</div>
)}
</div>
)
}
Displaying Rule Metadata
Rule metadata contains structured information about the template rule that produced the generated content. It varies by rule type and is most detailed fortemplate_table rules. This data is useful for displaying template instructions, table structure, and data extraction context alongside the generated output.
Rule Metadata by Rule Type
Fortemplate_text rules:
ruleMetadata = {
rule_type: "template_text", // The rule type
section_id: "uuid", // Template section ID
section_title: "Section Name", // Template section name
user_instructions: "...", // Primary instructions for generation
data_instructions: "...", // Data extraction/handling instructions
}
template_table rules (most detailed):
ruleMetadata = {
rule_type: "template_table",
section_id: "uuid",
section_title: "Section Name",
user_instructions: "...",
data_instructions: "...",
table_context_instructions: "...", // Context-specific table instructions
template_table: { // Full table definition
template_table_id: "uuid", // Unique table definition ID
blueprint_rule_id: "uuid", // Blueprint rule identifier
table_structure_html: "<table>...</table>", // HTML structure of the template table
table_caption: "Table 1: ...", // Table title/caption
user_instructions: "...", // Table-specific instructions
table_context_instructions: "...",
repeat_key: "study_arm", // Key for identifying repeating rows
data_instructions: "...", // Data extraction guidance
expansion_notes: "...", // Notes on expansion logic
can_spawn_multiple: true, // Whether multiple table instances can be generated
spawn_multiple_instructions: "..." // Instructions for generating multiple instances
}
}
summarize_content / copy_paste rules:
ruleMetadata = {
rule_type: "summarize_content",
section_id: "uuid",
section_title: "Section Name",
section_index: 0, // 0-based position in sections
rule_index: 0, // 0-based position in section rules
step_name: "rule_execution", // Processing step name
}
intra_document_summarize rules:
ruleMetadata = {
rule_type: "intra_document_summarize",
section_id: "uuid",
section_title: "Section Name",
target_section_ids_for_summary: [ // UUIDs of sections being summarized
"section-uuid-1",
"section-uuid-2"
]
}
React Rule Metadata Component
function RuleMetadataPanel({ metadata }) {
if (!metadata) return null
return (
<div className="rule-metadata-panel">
<h4>Rule Metadata</h4>
{/* Instructions */}
{metadata.user_instructions && (
<div className="metadata-field">
<label>Instructions</label>
<p>{metadata.user_instructions}</p>
</div>
)}
{metadata.data_instructions && (
<div className="metadata-field">
<label>Data Instructions</label>
<p>{metadata.data_instructions}</p>
</div>
)}
{metadata.table_context_instructions && (
<div className="metadata-field">
<label>Table Context</label>
<p>{metadata.table_context_instructions}</p>
</div>
)}
{/* Template table structure (for template_table rules) */}
{metadata.template_table && (
<TemplateTableMetadata table={metadata.template_table} />
)}
{/* Summary target sections (for intra_document_summarize rules) */}
{metadata.target_section_ids_for_summary?.length > 0 && (
<div className="metadata-field">
<label>Summarizes Sections</label>
<ul>
{metadata.target_section_ids_for_summary.map(id => (
<li key={id}>{id}</li>
))}
</ul>
</div>
)}
</div>
)
}
function TemplateTableMetadata({ table }) {
return (
<div className="template-table-metadata">
<h5>Template Table Definition</h5>
{table.table_caption && (
<div className="metadata-field">
<label>Caption</label>
<p>{table.table_caption}</p>
</div>
)}
{table.table_structure_html && (
<div className="metadata-field">
<label>Table Structure</label>
<div
className="table-structure-preview"
dangerouslySetInnerHTML={{ __html: table.table_structure_html }}
/>
</div>
)}
{table.repeat_key && (
<div className="metadata-field">
<label>Repeat Key</label>
<code>{table.repeat_key}</code>
</div>
)}
{table.can_spawn_multiple && (
<div className="metadata-field">
<label>Can Generate Multiple</label>
<p>Yes{table.spawn_multiple_instructions
? ` - ${table.spawn_multiple_instructions}`
: ''}</p>
</div>
)}
{table.expansion_notes && (
<div className="metadata-field">
<label>Expansion Notes</label>
<p>{table.expansion_notes}</p>
</div>
)}
</div>
)
}
Full Workflow: Section Navigation with Rule Details
This example shows the complete flow: list all sections, select one, fetch its rules, and display everything.import {
getDocumentSections,
getSectionRules,
generateProxyUrl
} from '@artosai/sdk'
async function buildDocumentRulesView(documentId) {
const accessToken = process.env.ARTOS_ACCESS_TOKEN
const baseUrl = 'https://api.artosai.com'
// Step 1: Get all sections
const { sections } = await getDocumentSections({
documentId, accessToken, baseUrl
})
// Step 2: For each section, fetch rules
const sectionRules = await Promise.all(
sections.map(async (section) => {
const { rules } = await getSectionRules({
sectionId: section.sectionId,
accessToken,
baseUrl
})
return { section, rules }
})
)
// Step 3: Process results
for (const { section, rules } of sectionRules) {
console.log(`\nSection: ${section.sectionTitle}`)
console.log(` Rules: ${rules.length}`)
const templateRules = rules.filter(r =>
['template_text', 'template_table'].includes(r.ruleType)
)
const extractionRules = rules.filter(r =>
['copy_paste', 'summarize_content'].includes(r.ruleType)
)
console.log(` Template rules: ${templateRules.length}`)
console.log(` Extraction rules: ${extractionRules.length}`)
// Show template URL if available
if (templateRules.length > 0 && templateRules[0].templateUrl) {
console.log(` Template URL: ${templateRules[0].templateUrl}`)
}
// Show breadcrumb summary
for (const rule of rules) {
if (rule.breadcrumbs?.trace_display?.discrepancies?.length > 0) {
console.log(` WARNING: Rule #${rule.orderIndex} has ${
rule.breadcrumbs.trace_display.discrepancies.length
} flagged issue(s)`)
}
}
}
return sectionRules
}
Filtering Rules by Type
A common pattern is filtering rules by category to render different UI components for each type.function categorizeRules(rules) {
return {
// Template rules: content generated from template instructions
// These rules have templateUrl and typically sourceChunks
templateText: rules.filter(r => r.ruleType === 'template_text'),
templateTable: rules.filter(r => r.ruleType === 'template_table'),
// Extraction rules: content extracted from source documents
// These rules have sourceChunk (singular) with presigned URLs
copyPaste: rules.filter(r => r.ruleType === 'copy_paste'),
summarize: rules.filter(r => r.ruleType === 'summarize_content'),
// All other rule types
other: rules.filter(r => ![
'template_text', 'template_table',
'copy_paste', 'summarize_content'
].includes(r.ruleType))
}
}
// Usage
const { rules } = await getSectionRules({ sectionId, accessToken, baseUrl })
const categorized = categorizeRules(rules)
// Render template rules with template comparison view
categorized.templateText.forEach(rule => {
renderTemplateComparison(rule.ruleTemplateText, rule.generatedContent, rule.templateUrl)
})
// Render extraction rules with source document links
categorized.copyPaste.forEach(rule => {
renderExtractionCard(rule.generatedContent, rule.sourceChunk)
})