struai
v2.6.1
Published
StruAI Drawing Analysis SDK - AI-powered construction drawing analysis
Maintainers
Readme
StruAI JavaScript SDK
Official JavaScript/TypeScript SDK for the StruAI Drawing Analysis API.
- Endpoint + response-shape reference:
../docs/HTTP_ENDPOINTS.md
Installation
npm install struaiEnvironment
export STRUAI_API_KEY=your_api_key
# Optional: defaults to https://api.stru.ai (SDK appends /v1 automatically)
export STRUAI_BASE_URL=https://api.stru.aiQuick Start
import { StruAI } from 'struai';
const client = new StruAI({
apiKey: process.env.STRUAI_API_KEY!,
baseUrl: process.env.STRUAI_BASE_URL, // optional
timeout: 60_000, // optional
});
const drawing = await client.drawings.analyze('/absolute/path/to/structural.pdf', { page: 12 });
const project = await client.projects.create({ name: 'Building A', description: 'Structural set' });
const jobOrBatch = await project.sheets.add(null, {
page: 12,
fileHash: await client.drawings.computeFileHash('/absolute/path/to/structural.pdf'),
});
const search = await project.docquery.search('beam connection', { limit: 5 });
console.log(search.hits.length);
const review = await client.reviews.create({
fileHash: await client.drawings.computeFileHash('/absolute/path/to/structural.pdf'),
pages: '12,13',
projectIds: [project.id],
customInstructions: 'Focus on cross-sheet coordination.',
});
const finalReview = await review.wait({ timeoutMs: 900_000, pollIntervalMs: 5_000 });
console.log(finalReview.status, (await review.issues()).length);
const customReview = await client.reviews.create({
fileHash: await client.drawings.computeFileHash('/absolute/path/to/structural.pdf'),
pages: '13',
projectIds: [project.id],
scout: 'Route broken references to Cross Reference Checker and buildability gaps to Constructability Reviewer.',
specialistsCommon: 'All specialists should stay grounded in DocQuery/search-docs evidence.',
specialists: [
{ name: 'Cross Reference Checker', instructions: 'Trace all detail, section, and sheet references.' },
{ name: 'Constructability Reviewer', instructions: 'Focus on missing dimensions and buildability gaps.' },
],
});
console.log(customReview.id);
const uploadReview = await client.reviews.create({
file: '/absolute/path/to/structural.pdf',
pages: 12,
});
console.log(uploadReview.id);Real Workflow Examples
npm install
npm run build
# Drawings-only flow
STRUAI_API_KEY=... STRUAI_BASE_URL=https://api.stru.ai \
STRUAI_PDF=/absolute/path/to/structural.pdf STRUAI_PAGE=12 \
node scripts/drawings_quickstart.mjs
# Full projects + docquery workflow
STRUAI_API_KEY=... STRUAI_BASE_URL=https://api.stru.ai \
STRUAI_PDF=/absolute/path/to/structural.pdf STRUAI_PAGE=12 \
node scripts/projects_workflow.mjs
# Optional cleanup after full workflow
STRUAI_CLEANUP=1 node scripts/projects_workflow.mjs
# Review workflow (start + refresh)
STRUAI_API_KEY=... STRUAI_BASE_URL=https://api.stru.ai \
STRUAI_REVIEW_FILE_HASH=your_file_hash STRUAI_REVIEW_PAGES=13 \
node scripts/reviews_workflow.mjs
# Review workflow (wait for terminal status)
STRUAI_API_KEY=... STRUAI_BASE_URL=https://api.stru.ai \
STRUAI_REVIEW_FILE_HASH=your_file_hash STRUAI_REVIEW_PAGES=13 STRUAI_REVIEW_WAIT=1 \
node scripts/reviews_workflow.mjs
# Review workflow (custom scout + custom specialist team)
STRUAI_API_KEY=... STRUAI_BASE_URL=https://api.stru.ai \
STRUAI_REVIEW_FILE_HASH=your_file_hash STRUAI_REVIEW_PAGES=13 \
STRUAI_REVIEW_SCOUT_FILE=/absolute/path/to/examples/prompts/page13_review/scout.md \
STRUAI_REVIEW_SPECIALISTS_COMMON_FILE=/absolute/path/to/examples/prompts/page13_review/specialists_common.md \
STRUAI_REVIEW_SPECIALISTS_FILE=/absolute/path/to/examples/prompts/page13_review/specialists.json \
node scripts/reviews_workflow.mjsSee scripts/README.md for quick copy/paste commands.
API Reference
Client
new StruAI({ apiKey, baseUrl?, timeout? })client.drawingsclient.projectsclient.reviews
Drawings (client.drawings)
analyze(file, { page, fileHash? }) -> Promise<DrawingResult>- Pass either
fileorfileHash. filecan be a file path string,Blob,ArrayBuffer, or typed array.
- Pass either
checkCache(fileHash) -> Promise<DrawingCacheStatus>computeFileHash(file) -> Promise<string>
Projects Top-Level (client.projects)
create({ name, description? }) -> Promise<ProjectInstance>list() -> Promise<Project[]>open(projectId, { name?, description? }?) -> ProjectInstancedelete(projectId) -> Promise<ProjectDeleteResult>
Reviews Top-Level (client.reviews)
create({ file?, pages, fileHash?, projectIds?, scout?, specialistsCommon?, specialists?, customInstructions? }) -> Promise<ReviewInstance>- Pass exactly one of
fileorfileHash. - Throws if both are missing or both are provided.
specialistsmust be a non-empty array of{name, instructions}objects when provided.- When
specialistsis provided,scoutis also required and must reference each specialist by exact name. - Omit
scout,specialistsCommon, andspecialiststo use the default review team.
- Pass exactly one of
list({ status? }?) -> Promise<Review[]>get(reviewId) -> Promise<ReviewInstance>open(reviewId) -> ReviewInstance
Project Instance (project)
Properties:
id,name,description,datasheets,docquery
Methods:
delete() -> Promise<ProjectDeleteResult>
Review Instance (review)
Properties:
id,data
Methods:
refresh() -> Promise<Review>status() -> Promise<Review>wait({ timeoutMs?, pollIntervalMs? }?) -> Promise<Review>- Rejects if the review reaches
failed. - Rejects if the timeout elapses first.
- Rejects if the review reaches
questions() -> Promise<ReviewQuestion[]>issues() -> Promise<ReviewIssue[]>
Sheets (project.sheets)
add(file, { page, fileHash?, sourceDescription?, onSheetExists?, communityUpdateMode?, semanticIndexUpdateMode? }) -> Promise<Job | JobBatch>pagesupports12,'1,3,5-7','all'- Pass either
fileorfileHash
delete(sheetId) -> Promise<SheetDeleteResult>job(jobId, { page? }?) -> Job
DocQuery (project.docquery)
nodeGet(uuid) -> Promise<DocQueryNodeGetResult>sheetEntities(sheetId, { entityType?, limit? }?) -> Promise<DocQuerySheetEntitiesResult>search(query, { index?, limit? }?) -> Promise<DocQuerySearchResult>neighbors(uuid, { mode?, direction?, relationshipType?, radius?, limit? }?) -> Promise<DocQueryNeighborsResult>cypher(query, { params?, maxRows? }?) -> Promise<DocQueryCypherResult>sheetSummary(sheetId, { orphanLimit? }?) -> Promise<DocQuerySheetSummaryResult>sheetList() -> Promise<DocQuerySheetListResult>referenceResolve(uuid, { limit? }?) -> Promise<DocQueryReferenceResolveResult>crop({ output, uuid?, bbox?, pageHash? }) -> Promise<DocQueryCropResult>
CLI parity: project-list maps to client.projects.list(), and the remaining 9 commands map to project.docquery.*, for full 10-command parity.
const project = client.projects.open('proj_86c0f02e');
const cypher = await project.docquery.cypher(
'MATCH (n:Entity {project_id:$project_id}) RETURN count(n) AS total',
{ params: {}, maxRows: 1 }
);
const crop = await project.docquery.crop({
uuid: 'entity-uuid-here',
output: '/absolute/path/to/crop.png',
});
console.log(cypher.records[0]?.total, crop.output_path, crop.bytes_written);Jobs
Job:
id,pagestatus() -> Promise<JobStatus>wait({ timeoutMs?, pollIntervalMs? }?) -> Promise<SheetResult>
JobBatch:
jobs,idsstatusAll() -> Promise<JobStatus[]>waitAll({ timeoutMs?, pollIntervalMs? }?) -> Promise<SheetResult[]>
Reviews
Review:
review_id,status,total_pages,pages,progressis_running,is_complete,is_partial,is_failed,is_terminal- Status values:
running,completed,completed_partial,failed pagesandtotal_pagesare populated byPOST /v1/reviews; laterrefresh()/get()calls reflect the slimmerGET /v1/reviews/{review_id}payload and may omit them.progress.specialist.activecontains live in-progress specialist rows withquestion_id,agent,turns_used,max_turns, andupdated_atwhen the server exposes them.
ReviewQuestion:
- Includes
raw_model_output, preserved as nested JSON when present.
Endpoint Coverage
Full endpoint + response-shape reference: ../docs/HTTP_ENDPOINTS.md
Tier 1:
POST /v1/drawingsGET /v1/drawings/cache/{file_hash}
Tier 2:
POST /v1/projectsGET /v1/projectsDELETE /v1/projects/{project_id}POST /v1/projects/{project_id}/sheetsDELETE /v1/projects/{project_id}/sheets/{sheet_id}GET /v1/projects/{project_id}/jobs/{job_id}GET /v1/projects/{project_id}/node-getGET /v1/projects/{project_id}/sheet-entitiesGET /v1/projects/{project_id}/searchGET /v1/projects/{project_id}/neighborsPOST /v1/projects/{project_id}/cypherPOST /v1/projects/{project_id}/crop
Tier 3:
POST /v1/reviewsGET /v1/reviewsGET /v1/reviews/{review_id}GET /v1/reviews/{review_id}/questionsGET /v1/reviews/{review_id}/issues
License
MIT
