bx-ai-chatting — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited bx-ai-chatting (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
BoxLang is the AI-native software productivity platform for building, modernizing and running applications, with developers and AI agents working together.
aiChat()// Signature
aiChat( messages, params={}, options={}, headers={} )messages: string, a message struct, an array of message structs, or an aiMessage() objectparams: model parameters sent to the provider (temperature, max_tokens, model, top_p, stop, etc.)options: provider, apiKey, timeout (seconds, module default 90), returnFormat, logRequest, logResponse, logRequestToConsole, logResponseToConsoleheaders: struct of extra HTTP headers for the requestreturnFormat: "single")// Uses the configured default provider
answer = aiChat( "What is the capital of France?" )
// With parameters
code = aiChat(
"Write a Fibonacci function in BoxLang",
{ temperature: 0.2, max_tokens: 500 }
)
// With a specific provider
result = aiChat(
"Summarize this text: ...",
{ temperature: 0.5 },
{ provider: "claude" }
)params = {
temperature : 0.7, // 0.0 (deterministic) up to creative
max_tokens : 1000, // max response length
model : "gpt-4o", // provider-specific model name
top_p : 1.0, // nucleus sampling (use OR temperature, not both)
stop : ["\n\n"] // stop sequences
}Params are passed through to the provider, so valid names depend on the provider.
Valid options.returnFormat strings: single (default), all, raw, json, xml. Anything else throws InvalidArgument.
| Value | Returns |
|---|---|
single | The message content string |
all | Array of choices (OpenAI-shaped providers); for claude, the array of content blocks |
raw | The full provider response (struct) |
json | Parsed JSON from the reply (struct/array); empty struct if none is found |
xml | Parsed XML document |
text = aiChat( "Hello" )
// Full provider response (OpenAI-shaped providers)
response = aiChat( "Hello", {}, { returnFormat: "raw" } )
println( response.choices.first().message.content )
println( response.usage.total_tokens ) // usage keys: prompt_tokens, completion_tokens, total_tokens
// A class, struct, or array may also be passed as returnFormat for structured outputraw returns the provider's own envelope, so for claude it is Anthropic's native shape, not choices[].
messages = [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "What is 2+2?" },
{ role: "assistant", content: "4" },
{ role: "user", content: "Multiply that by 10." }
]
result = aiChat( messages )aiMessage() is a fluent builder; the method name is the role, and bind() fills ${placeholders}:
msg = aiMessage()
.system( "You are a helpful assistant." )
.user( "Tell me about ${topic}" )
.bind( { topic: "BoxLang" } )
result = aiChat( msg.render() )// Non-blocking, returns a BoxLang Future
future = aiChatAsync( "Explain quantum entanglement" )
// Do other work here...
response = future.get()aiChatAsync() takes the same arguments as aiChat().
// aiChatStream( messages, callback, params={}, options={}, headers={} )
aiChatStream(
"Write a short story about a robot",
( chunk ) => {
// chunk is an OpenAI-shaped chat.completion.chunk struct
print( chunk.choices?.first()?.delta?.content ?: "" )
},
{ temperature: 0.7 },
{ provider: "openai" }
)aiChatStream() returns nothing. It has no returnFormat.delta.content may be absent on some chunks (role, reasoning, or usage chunks), so default it with ?: "".Reasoning/thinking text is surfaced on one standard key regardless of provider, and is never merged into content.
chunk.choices.first().delta.reasoning. Providers that emit reasoning_content (for example DeepSeek) or thinking are mapped onto delta.reasoning. Claude extended thinking is emitted as delta.reasoning chunks before the text.response.choices[ i ].message.reasoning, available with returnFormat: "raw" or "all". reasoning_content and thinking are mapped onto it.returnFormat: "raw": response.reasoning at the top level of the native envelope (only present when the model produced thinking blocks).?. and a default.aiChatStream( "Solve 17 * 24", ( chunk ) => {
var delta = chunk.choices?.first()?.delta ?: {}
if( delta.keyExists( "reasoning" ) ) print( "[thinking] " & delta.reasoning )
if( delta.keyExists( "content" ) ) print( delta.content ?: "" )
}, {}, { provider: "deepseek" } )result = aiChat( "Hello", {}, { provider: "openai", apiKey: "sk-..." } )
result = aiChat( "Hello", {}, { provider: "claude" } )
result = aiChat( "Hello", {}, { provider: "ollama" } ) // localThe provider must support chat, otherwise UnsupportedCapability is thrown. If no apiKey is passed, the <PROVIDER>_API_KEY environment setting is used, then the module apiKey setting. Valid provider keys and their capabilities are in the bx-ai-models skill. For speech and transcription, see the bx-ai-audio skill.
There is no exceptions package. Errors are thrown as plain BoxLang exceptions with a type:
ProviderError: a streaming request failed (thrown from aiChatStream())JsonDeserializationError: the provider response was not valid JSONProviderNotSupported: unknown provider keyUnsupportedCapability: the provider cannot chatInvalidArgument: invalid returnFormattry {
result = aiChat( "Hello", {}, { provider: "openai" } )
} catch ( any e ) {
if( e.type == "ProviderNotSupported" ) {
logError( "Bad provider: #e.message#" )
} else {
logError( "AI error [#e.type#]: #e.message#" )
}
}Failures and rate limits also announce the onAIError and onAIRateLimitHit interception points.
model: as a top-level BIF argument: put it in paramstemperature and top_p simultaneouslyaiChatStream(): it is the 2nd argumentreturnFormat: "full" or "choices": they are not valid valuesreturnFormat: "raw" when you need token counts, finish reasons or reasoningaiChatAsync() for long-running requests in web handlers~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.