Chapter 9: Modules and Code Organization #
How to declare modules, import dependencies with aliases and
selective imports, and control visibility with pub, crate, and super. How to
re-export modules with pub use. How to navigate the Neam standard library. How to
structure a Neam project with neam.toml. How to use the neam-pkg package manager
to create, install, and publish reusable libraries. How to design modular agent
libraries that teams can share across projects.
Think of modules like departments in a company. The marketing team does not need to know how the engineering team writes code, and the finance department should not have direct access to every internal engineering document. But when marketing needs a product spec, there is a clear, agreed-upon way to request it. Modules work the same way: each one has its own responsibilities, keeps its internal details private, and collaborates with other modules through well-defined public interfaces. Without this structure, your codebase becomes like a company where everyone shares one giant desk -- chaotic, fragile, and impossible to scale.
In this chapter, you will learn how Neam's module system brings that organizational clarity to your code, from single-file scripts all the way to published packages used by teams around the world.
Why Modules? #
As your Neam programs grow beyond a single file, you need a way to organize code into logical units, control what is visible to the outside world, and reuse components across projects. Without modules, every function, every agent definition, and every helper lives in a single global scope -- a recipe for name collisions, tangled dependencies, and unmaintainable codebases.
Neam's module system draws inspiration from Rust and Go: modules map to files, visibility
is explicit, and dependencies are declared in a manifest file (neam.toml). This chapter
takes you from single-file programs to well-organized, multi-file projects with reusable
packages.
Module Declaration #
Every Neam source file can declare which module it belongs to using the module keyword
at the top of the file:
module std.rag.error;
The module path uses dot-separated identifiers. By convention, the path mirrors the directory structure:
src/
rag/
error.neam --> module myproject.rag.error
config.neam --> module myproject.rag.config
retriever.neam --> module myproject.rag.retriever
ingest/
error.neam --> module myproject.ingest.error
pipeline.neam --> module myproject.ingest.pipeline
If a file does not include a module declaration, it belongs to the root (unnamed) module.
This is typical for entry-point files like src/main.neam.
Naming Conventions #
- Use lowercase, dot-separated paths:
std.rag.config, notStd.RAG.Config. - The first segment typically identifies the package or namespace:
stdfor the standard library, your project name for application code. - Keep module names short and descriptive:
agents.base, notagents.base_agent_utilities_and_helpers.
Importing Modules #
The import Statement #
Use import to bring a module into scope. After importing, you access its public members
through the module's last path segment:
import std.rag.error;
{
let err = error.config_error("model", "Model is required");
emit str(err);
}
Here, import std.rag.error makes the module available as error. You then call
error.config_error(...) to access the public function.
Import Aliases #
When two modules have the same final segment, or when you want a shorter name, use
import ... as to create an alias:
import std.rag.error as rag_err;
import std.ingest.error as ingest_err;
{
let e1 = rag_err.config_error("model", "Model is required");
let e2 = ingest_err.parse_error("pdf", "Invalid format");
}
Selective Imports #
You can import specific items from a module using curly braces:
import std.math::{sin, cos, pi};
{
let angle = pi / 4;
emit "sin(pi/4) = " + str(sin(angle));
emit "cos(pi/4) = " + str(cos(angle));
}
This brings only sin, cos, and pi into scope -- not the entire std.math module.
The use Statement #
The use statement is an alternative that brings the module into scope. In practice,
use and import behave similarly in Neam. The convention is:
importfor external or standard library modules.usefor modules within your own project or closely related packages.
module myproject.rag.config;
use myproject.rag.error;
pub fun build_config(config) {
if (config.retriever == nil) {
return { ok: false, error: error.config_error("retriever", "Retriever is required") };
}
return { ok: true, value: config };
}
The config module uses myproject.rag.error to create structured error values.
Import Patterns at a Glance #
| Syntax | Effect |
|---|---|
import std.math; |
Access as math.sin(...) |
import std.math as m; |
Access as m.sin(...) |
import std.math::{sin, cos}; |
Access as sin(...), cos(...) directly |
use myproject.utils; |
Same as import, for intra-project modules |
Visibility: pub, crate, and super #
Not everything in a module should be accessible to the outside world. Neam uses visibility modifiers to control access.
pub -- Public Visibility #
Functions, variables, and other declarations marked with pub are accessible from any
module that imports the containing module:
module myproject.agents.base;
// Public: accessible from anywhere that imports this module
pub fun create_agent(kind, config) {
return {
kind: kind,
config: config,
id: generate_agent_id(),
state: agent_state_idle()
};
}
// Public states
pub fun agent_state_idle() { return { kind: "Idle" }; }
pub fun agent_state_running() { return { kind: "Running" }; }
pub fun agent_state_error(message) { return { kind: "Error", message: message }; }
// Private: only accessible within this file
fun generate_agent_id() {
return "agent_" + str(random(100000));
}
In this example, create_agent, agent_state_idle, and agent_state_running are
public. The helper generate_agent_id is private -- it has no pub keyword, so only
code within the same module can call it.
Default (Private) Visibility #
Any declaration without a visibility modifier is private to its module. This is the most restrictive level:
module myproject.utils;
// Private: only usable within myproject.utils
fun internal_helper(data) {
return data + "_processed";
}
// Public: usable by anyone who imports myproject.utils
pub fun process(data) {
return internal_helper(data);
}
crate -- Package-Level Visibility #
The crate modifier makes a declaration visible to all modules within the same package
(defined by the same neam.toml), but not to external packages that depend on yours:
module myproject.agents.internal;
// Visible within the myproject package, but not to external consumers
crate fun reset_agent_state(agent) {
agent.state = { kind: "Idle" };
return agent;
}
This is useful for shared internals that multiple modules in your project need, but that should not be part of your public API.
super -- Parent Module Access #
The super keyword refers to the parent module. It is useful when a submodule needs to
access something from its parent without spelling out the full path:
module myproject.agents.helpers;
use super.base; // Refers to myproject.agents.base
pub fun create_default_agent() {
return base.special_agent_base("default", {});
}
Visibility Summary #
| Modifier | Accessible From |
|---|---|
pub |
Any module that imports the declaring module |
| (none) | Only the declaring module (private) |
crate |
Any module in the same package |
super |
Used in use to reference the parent module |
Re-Exporting with pub use #
Sometimes a module serves as a facade -- it re-exports items from other modules to
provide a simpler public API. The pub use statement makes an imported module's public
items available through the re-exporting module:
// File: src/lib.neam
module mylib;
// Re-export these modules as part of mylib's public API
pub use mylib.agents;
pub use mylib.config;
pub use mylib.utils;
Consumers can now import from mylib directly:
import mylib.agents;
import mylib.config;
This pattern keeps your internal module structure flexible -- you can reorganize files without breaking the public API, as long as the re-exports remain the same.
The Standard Library #
Neam ships with a comprehensive standard library organized into focused modules. You
access these through the std namespace. Here is an overview of what is available:
| Namespace | Purpose | Key Functions |
|---|---|---|
std.agents |
Agent utilities | base, handoffs, prompts, serving |
std.ai |
AI/ML primitives | embed(), classify(), generate() |
std.async |
Asynchronous operations | resolve(), all(), delay() |
std.audio |
Audio I/O | record(), play(), stream() |
std.collections |
Lists, maps, sets | push(), filter(), fold(), keys(), to_set() |
std.core |
Option and Result types | some(), none(), ok(), err(), unwrap() |
std.crypto |
Cryptography | sha256(), uuid_v4(), base64_encode() |
std.data |
Data formats (JSON, CSV) | parse(), stringify(), pretty() |
std.graders |
Agent output grading | grade(), rubric(), score() |
std.ingest |
Document ingestion | pipeline, parser, store |
std.io |
File I/O | read_file(), write_file() |
std.math |
Mathematical functions | sin(), cos(), sqrt(), abs(), pi |
std.media |
Image and video utilities | resize(), thumbnail(), transcode() |
std.net |
HTTP client and networking | get(), post(), request() |
std.observability |
Tracing and monitoring | trace(), span(), metrics() |
std.project |
Project metadata access | name(), version(), config() |
std.rag |
RAG pipeline | retriever, config, document |
std.realtime |
Real-time streaming | stream(), subscribe(), emit() |
std.schemas |
Schema definitions | define(), validate(), to_json_schema() |
std.speech |
Speech-to-text / TTS | transcribe(), synthesize() |
std.testing |
Test framework | assert_eq(), assert_ok(), test() |
std.text |
String utilities | split(), join(), trim(), replace() |
std.time |
Date and time | now(), format(), duration() |
std.trust |
Trust and safety primitives | sanitize(), validate(), audit() |
std.vad |
Voice activity detection | detect(), is_speech(), threshold() |
std.voice |
Voice agent support | listen(), speak(), voice_loop() |
Using Standard Library Modules #
import std.net.http;
import std.data.json;
import std.core.result;
fun fetch_weather(city) {
let url = "https://api.weather.example.com/v1/" + city;
try {
let response = http.get(url);
let data = json.parse(response);
return result.ok(data);
} catch (err) {
return result.err("Failed to fetch weather: " + str(err));
}
}
The standard library follows the same module conventions as your own code: every public
function is marked with pub, internal helpers are private, and modules are organized
into clear namespaces. As you progress through this book, you will use many of these
modules in agent systems.
Standard Library Sampler
Write a short Neam program that imports three different standard library modules and uses at least one function from each. For example:
- Use
std.textto split a sentence into words. - Use
std.collections.listto filter only words longer than 4 characters. - Use
std.data.jsonto stringify the result and emit it.
Bonus: wrap your logic in a pub fun inside a module called myproject.text_demo and
import it from a separate main.neam file.
Project Structure with neam.toml #
Every Neam project has a neam.toml file at its root. This file defines the project
metadata, dependencies, build configuration, and more. Here is the complete manifest
from the Neam sample project:
# Neam Project Manifest
neam_version = "1.0"
# ============================================
# Project Metadata
# ============================================
[project]
name = "ai-assistant"
version = "0.1.0"
type = "binary"
description = "An AI-powered assistant using Neam"
authors = ["Developer <dev@example.com>"]
license = "MIT"
[project.entry_points]
main = "src/main.neam"
api = "src/api.neam"
# ============================================
# Dependencies
# ============================================
[dependencies]
# Version-based dependency
# utils = "1.0.0"
# Git-based dependency
# ai-tools = { git = "https://github.com/neam/ai-tools" }
# Dependency with features
# ml-lib = { version = "2.0.0", features = ["gpu", "advanced"] }
[dev-dependencies]
# test-utils = "0.1.0"
# ============================================
# Scripts
# ============================================
[scripts]
build = "neamc src/main.neam -o build/main.neamb"
run = "neam build/main.neamb"
test = "neam-cli test tests/**/*.neam"
api = "neam-api --port 8080"
clean = "rm -rf build/"
# ============================================
# Features (Conditional Compilation)
# ============================================
[features]
default = ["basic-agents"]
basic-agents = []
advanced-rag = ["hyde", "self-rag"]
multi-provider = ["openai", "ollama", "anthropic"]
# ============================================
# Agent Configuration
# ============================================
[agent]
provider = "openai"
model = "gpt-4o-mini"
capabilities = ["text-generation", "code-generation"]
[agent.limits]
max-tokens-per-request = 4096
max-concurrent-tools = 5
timeout-seconds = 300
max-retries = 3
# ============================================
# Test Configuration
# ============================================
[test]
timeout = 60
parallel = true
coverage = true
coverage-threshold = 80
include = ["tests/**/*.neam"]
exclude = ["tests/fixtures/**"]
Key Sections #
[project] and [package] -- Name, version, type (binary or library), description,
and license. The entry_points table maps named targets to source files.
The [package] key is also accepted as an alternative to [project]
in your manifest. Both are recognized by the toolchain. The [package] key is the
preferred convention for published libraries, while [project] remains common for
applications. Here is an example using [package]:
[package]
name = "agent-utils"
version = "1.0.0"
description = "Utility functions for Neam agents"
keywords = ["agents", "utilities"]
categories = ["agent-development"]
repository = "https://github.com/neam-lang/agent-utils"
[dependencies] -- External packages your project depends on. Three formats are
supported:
- Version string: utils = "1.0.0"
- Git repository: ai-tools = { git = "https://github.com/neam/ai-tools" }
- Version with features: ml-lib = { version = "2.0.0", features = ["gpu"] }
[dev-dependencies] -- Packages needed only for development and testing.
[scripts] -- Named commands you can run with neam-cli run <script-name>.
[features] -- Conditional compilation flags. Enable or disable features at build time.
[agent] -- Default agent configuration shared across the project.
[test] -- Test runner configuration: timeout, parallelism, coverage thresholds.
Standard Project Layout #
The following directory structure is the recommended layout for Neam projects:
my-agent-project/
neam.toml # Project manifest
src/
main.neam # Main entry point
api.neam # Alternative entry point (API server)
agents/
triage.neam # Triage agent module
specialist.neam # Specialist agents module
helpers.neam # Shared agent helpers
utils/
validation.neam # Input validation utilities
formatting.neam # Output formatting utilities
tests/
test_agents.neam # Agent integration tests
test_utils.neam # Unit tests for utilities
docs/
README.md # Project documentation
build/
main.neamb # Compiled bytecode (generated)
Building a Multi-File Project #
Let us walk through creating a project from scratch.
Step 1: Initialize the Project #
neam-pkg init customer-support
cd customer-support
This creates the directory structure with a neam.toml and a src/main.neam stub.
Step 2: Define Shared Utilities #
Create src/utils/validation.neam:
module customer_support.utils.validation;
pub fun validate_email(email) {
if (email == nil) {
return {"ok": false, "error": "Email is required"};
}
if (!email.contains("@")) {
return {"ok": false, "error": "Invalid email format"};
}
return {"ok": true, "value": email};
}
pub fun validate_query(query) {
if (query == nil) {
return {"ok": false, "error": "Query is required"};
}
if (len(query) < 3) {
return {"ok": false, "error": "Query too short (minimum 3 characters)"};
}
if (len(query) > 1000) {
return {"ok": false, "error": "Query too long (maximum 1000 characters)"};
}
return {"ok": true, "value": query};
}
Step 3: Define Agent Modules #
Create src/agents/triage.neam:
module customer_support.agents.triage;
use customer_support.utils.validation;
agent TriageAgent {
provider: "openai"
model: "gpt-4o-mini"
system: "Classify queries into: BILLING, TECHNICAL, GENERAL. Reply with only the category."
}
pub fun classify(query) {
let valid = validation.validate_query(query);
if (!valid["ok"]) {
return valid;
}
try {
let category = TriageAgent.ask(query);
return {"ok": true, "value": category};
} catch (err) {
return {"ok": false, "error": "Triage failed: " + str(err)};
}
}
Create src/agents/specialist.neam:
module customer_support.agents.specialist;
use customer_support.utils.validation;
agent BillingAgent {
provider: "openai"
model: "gpt-4o-mini"
system: "You are a billing specialist. Help with payment and invoice questions."
}
agent TechnicalAgent {
provider: "openai"
model: "gpt-4o"
system: "You are a technical support specialist. Help with product issues."
}
agent GeneralAgent {
provider: "openai"
model: "gpt-4o-mini"
system: "You are a general support agent. Help with common questions."
}
pub fun handle(category, query) {
let valid = validation.validate_query(query);
if (!valid["ok"]) {
return valid;
}
try {
if (category == "BILLING") {
return {"ok": true, "value": BillingAgent.ask(query)};
}
if (category == "TECHNICAL") {
return {"ok": true, "value": TechnicalAgent.ask(query)};
}
return {"ok": true, "value": GeneralAgent.ask(query)};
} catch (err) {
return {"ok": false, "error": "Specialist failed: " + str(err)};
}
}
Step 4: Wire It Together in main.neam #
// File: src/main.neam
// Entry point -- no module declaration needed
import customer_support.agents.triage;
import customer_support.agents.specialist;
{
emit "=== Customer Support System ===";
let query = "Why was I double-charged this month?";
emit "Query: " + query;
// Step 1: Classify
let classification = triage.classify(query);
if (!classification["ok"]) {
emit "Error: " + classification["error"];
panic("Triage failed");
}
let category = classification["value"];
emit "Category: " + category;
// Step 2: Route to specialist
let response = specialist.handle(category, query);
if (response["ok"]) {
emit "Response: " + response["value"];
} else {
emit "Error: " + response["error"];
}
emit "=== Done ===";
}
Step 5: Build and Run #
# Compile
neamc src/main.neam -o build/main.neamb
# Run
neam build/main.neamb
The Package Manager: neam-pkg #
Neam includes a package manager for creating, installing, and publishing reusable libraries.
Creating a New Package #
neam-pkg init my-agent-lib --type library
This generates:
my-agent-lib/
neam.toml
src/
lib.neam # Library entry point
tests/
test_lib.neam
The neam.toml has type = "library" instead of type = "binary":
[project]
name = "my-agent-lib"
version = "0.1.0"
type = "library"
description = "A reusable agent library"
Installing Dependencies #
# Install a published package
neam-pkg install rag-utils
# Install from a Git repository
neam-pkg install --git https://github.com/neam-community/rag-utils
# Install a specific version
neam-pkg install rag-utils@2.1.0
After installation, the dependency is added to your neam.toml automatically:
[dependencies]
rag-utils = "2.1.0"
Publishing a Package #
# Verify the package builds and passes tests
neam-pkg check
# Publish to the Neam package registry
neam-pkg publish
Run neam-pkg check before every publish. It validates your neam.toml,
compiles all source files, runs tests, and checks that all public functions are
documented.
Common neam-pkg Commands #
| Command | Description |
|---|---|
neam-pkg init <name> |
Create a new project |
neam-pkg init <name> --type library |
Create a new library |
neam-pkg install <pkg> |
Install a dependency |
neam-pkg install --git <url> |
Install from Git |
neam-pkg update |
Update all dependencies |
neam-pkg remove <pkg> |
Remove a dependency |
neam-pkg check |
Validate, compile, and test |
neam-pkg publish |
Publish to registry |
neam-pkg search <query> |
Search the package registry |
neam-pkg list |
List installed packages |
neam-pkg outdated |
Show outdated packages |
neam-pkg link |
Link local package for development |
neam-pkg login |
Authenticate with registry |
neam-pkg docs <pkg> |
Open package documentation |
The Package Format (.neampkg) #
When you publish a package, neam-pkg bundles your source code, manifest, and metadata
into a .neampkg archive. Here is what the package contains:
The checksums.sha256 file ensures integrity during download, and the optional
signature.sig file allows cryptographic verification of the package author.
The Registry Ecosystem #
The package registry is the central hub that connects your local tooling to shared packages. Here is how the pieces fit together:
When you run neam-pkg install, the CLI resolves versions from the registry at
registry.neam.dev, downloads packages into your local cache at ~/.neam/packages/,
and writes a neam.lock file to pin exact versions. The lock file ensures that every
developer on your team -- and your CI pipeline -- uses identical dependency versions.
When you are building a library that is used by multiple local projects,
use neam-pkg link instead of publishing to the registry during development. This
command creates a symlink from your library's source directory into the consuming
project's dependency tree, so every change you make is immediately reflected without
re-publishing. Run neam-pkg link from the library directory, then
neam-pkg link <library-name> from the consuming project. When you are ready to
ship, replace the link with a versioned dependency in neam.toml.
Building Your First Package #
Let us walk through the complete lifecycle of creating, testing, and publishing a small library package from scratch.
Step 1: Create the Package
# Initialize a new library package
neam-pkg init text-toolkit --type library
cd text-toolkit
This scaffolds the standard library layout:
text-toolkit/
neam.toml
src/
lib.neam
tests/
test_lib.neam
Step 2: Define the Public API
Edit src/lib.neam to re-export your modules:
module text_toolkit;
pub use text_toolkit.core;
Step 3: Write the Core Logic
Create src/core.neam:
module text_toolkit.core;
// Count the number of words in a text string
pub fun word_count(text) {
if (text == nil || len(text) == 0) {
return 0;
}
let words = split(text, " ");
// Filter out empty strings from multiple spaces
let real_words = filter(words, fn(w) { return len(w) > 0; });
return len(real_words);
}
// Truncate text to a maximum length, appending "..." if shortened
pub fun truncate(text, max_length) {
if (text == nil) {
return "";
}
if (len(text) <= max_length) {
return text;
}
return slice(text, 0, max_length) + "...";
}
// Convert a string to a URL-friendly slug
pub fun slugify(text) {
if (text == nil) {
return "";
}
let lower = lowercase(text);
let slug = replace(lower, " ", "-");
return slug;
}
Step 4: Write Tests
Edit tests/test_lib.neam:
import text_toolkit.core;
test "word_count counts words correctly" {
assert_eq(core.word_count("hello world"), 2);
assert_eq(core.word_count("one"), 1);
assert_eq(core.word_count(""), 0);
assert_eq(core.word_count(nil), 0);
}
test "truncate shortens long text" {
assert_eq(core.truncate("hello world", 5), "hello...");
assert_eq(core.truncate("hi", 10), "hi");
assert_eq(core.truncate(nil, 5), "");
}
test "slugify creates URL-friendly strings" {
assert_eq(core.slugify("Hello World"), "hello-world");
assert_eq(core.slugify("Neam Is Great"), "neam-is-great");
assert_eq(core.slugify(nil), "");
}
Step 5: Validate and Publish
# Run the full check: validate manifest, compile, run tests
neam-pkg check
# Authenticate with the registry (first time only)
neam-pkg login
# Publish to the Neam package registry
neam-pkg publish
After publishing, anyone can install your package with:
neam-pkg install text-toolkit
Extend the Package
Add two more functions to text-toolkit:
reverse(text)-- returns the text with characters in reverse order.capitalize(text)-- returns the text with the first character uppercased and the rest unchanged.
Write tests for both functions, run neam-pkg check, and verify everything passes.
Then think about this: should reverse and capitalize live in core.neam, or
would a new module like text_toolkit.transform be a better home? What are the
trade-offs?
Creating Reusable Agent Libraries #
A well-designed agent library exposes a clean public API, hides implementation details, and is configurable through parameters rather than hardcoded values.
Example: A Reusable Summarization Library #
module summarizer;
// Re-export the public API
pub use summarizer.core;
pub use summarizer.config;
module summarizer.config;
pub fun default_config() {
return {
"provider": "openai",
"model": "gpt-4o-mini",
"max_length": 200,
"style": "concise"
};
}
pub fun with_model(config, model) {
config["model"] = model;
return config;
}
pub fun with_max_length(config, max_length) {
config["max_length"] = max_length;
return config;
}
pub fun with_style(config, style) {
config["style"] = style;
return config;
}
module summarizer.core;
use summarizer.config;
// This agent is configured at runtime based on the config
agent SummarizeAgent {
provider: "openai"
model: "gpt-4o-mini"
system: "Summarize the given text concisely."
}
pub fun summarize(text, cfg) {
if (cfg == nil) {
cfg = config.default_config();
}
let prompt = build_prompt(text, cfg);
try {
let result = SummarizeAgent.ask(prompt);
return {"ok": true, "value": result};
} catch (err) {
return {"ok": false, "error": "Summarization failed: " + str(err)};
}
}
pub fun summarize_batch(texts, cfg) {
let results = [];
for (text in texts) {
let result = summarize(text, cfg);
push(results, result);
}
return results;
}
// Private helper -- not exported
fun build_prompt(text, cfg) {
let prompt = "Summarize the following text";
if (cfg["style"] == "concise") {
prompt = prompt + " in a concise, bullet-point format";
} else if (cfg["style"] == "detailed") {
prompt = prompt + " in a detailed paragraph format";
}
if (cfg["max_length"] != nil) {
prompt = prompt + " (maximum " + str(cfg["max_length"]) + " words)";
}
prompt = prompt + ":\n\n" + text;
return prompt;
}
Using the Library from Another Project #
In the consuming project's neam.toml:
[dependencies]
summarizer = "1.0.0"
In the consuming project's code:
import summarizer.core;
import summarizer.config;
{
let cfg = config.default_config();
cfg = config.with_style(cfg, "detailed");
cfg = config.with_max_length(cfg, 100);
let article = "Neam is a programming language designed for building AI agent systems...";
let result = core.summarize(article, cfg);
if (result["ok"]) {
emit result["value"];
} else {
emit "Error: " + result["error"];
}
}
Library Design Guidelines #
1. Expose configuration through builder functions. Instead of requiring users to
construct config maps manually, provide default_config() and with_*() builder
functions. This pattern is used throughout the Neam standard library.
2. Return result maps from public functions. Do not throw exceptions from library
code. Return {ok: true/false, ...} so the consumer decides how to handle errors.
3. Keep agents private when possible. If your library creates agents internally, the consumer should not need to know about them. Expose a function-based API, not raw agent references.
4. Document your public API. Every pub fun should have a comment explaining its
parameters, return value, and error conditions.
5. Include tests. Ship a tests/ directory with your library. The neam-pkg check
command runs them automatically.
Feature Flags #
The [features] section of neam.toml enables conditional compilation. Different
consumers can enable different subsets of functionality:
[features]
default = ["basic-agents"]
basic-agents = []
advanced-rag = ["hyde", "self-rag"]
multi-provider = ["openai", "ollama", "anthropic"]
In consumer neam.toml:
[dependencies]
my-agent-lib = { version = "1.0.0", features = ["advanced-rag", "multi-provider"] }
This keeps the compiled output lean: features not enabled are not compiled into the final bytecode.
Summary #
This chapter covered the full lifecycle of code organization in Neam:
| Concept | Syntax / Tool |
|---|---|
| Module declaration | module myproject.utils; |
| Import | import std.rag.error; |
| Import alias | import std.rag.error as rag_err; |
| Selective import | import std.math::{sin, cos}; |
| Use (intra-project) | use myproject.utils; |
| Re-export | pub use mylib.agents; |
| Public visibility | pub fun ... |
| Package-level visibility | crate fun ... |
| Parent module reference | use super.base; |
| Project manifest | neam.toml |
| Create project | neam-pkg init <name> |
| Install dependency | neam-pkg install <pkg> |
| Publish package | neam-pkg publish |
| Link local package | neam-pkg link |
| Package format | .neampkg archive |
You also surveyed the Neam standard library (std.*), which provides ready-made
modules for agents, AI primitives, async operations, audio, collections, cryptography,
data formats, grading, ingestion, I/O, math, media, networking, observability, project
metadata, RAG, real-time streaming, schemas, speech, testing, text, time, trust and
safety, voice activity detection, and voice agents.
You now have all the language fundamentals needed to build real agent systems. Part II is complete. In Part III, you will use everything you have learned to declare your first AI agent, connect to LLM providers, and build multi-agent architectures.
Exercises #
Exercise 9.1: Module Extraction
Take the customer-support example from this chapter and add a new module:
customer_support.utils.formatting. Move any string formatting logic into this module
and expose it via pub fun declarations. Update the existing modules to import and use
the new formatting module.
Exercise 9.2: Visibility Audit
Review the following code and identify which functions should be pub, which should be
crate, and which should be private. Justify each choice.
module myproject.agents.router;
fun calculate_score(query, agent_description) {
// Internal scoring logic
return 0.85;
}
fun select_best_agent(scores) {
// Pick the highest scoring agent
let best = nil;
let best_score = 0;
// ...
return best;
}
fun route_query(query, agents) {
// Main routing logic
let scores = [];
// ...
return select_best_agent(scores);
}
fun reset_router_state() {
// Clear cached routing decisions
}
Exercise 9.3: Library from Scratch
Create a reusable Neam library called text-toolkit that provides:
1. A word_count(text) function.
2. A truncate(text, max_length) function that cuts text at max_length characters
and appends "..." if it was truncated.
3. A slugify(text) function that converts a string to lowercase and replaces spaces
with hyphens.
Write the neam.toml, the module file(s), and at least three tests. Follow the builder
pattern for any configuration.
Exercise 9.4: Dependency Graph Draw the module dependency graph (on paper or as ASCII art) for a project with these imports:
main.neam imports: agents.router, agents.specialist, utils.logging
agents/router imports: agents.specialist, utils.validation
agents/specialist imports: utils.validation, utils.formatting
utils/validation imports: (none)
utils/formatting imports: (none)
utils/logging imports: utils.formatting
Identify whether there are any circular dependencies. What would happen if
utils.validation tried to import agents.router?
Exercise 9.5: Feature Flags
Design a neam.toml for an agent library called smart-agents that supports these
optional features:
- openai -- enables OpenAI provider support.
- ollama -- enables Ollama provider support.
- rag -- enables RAG retrieval integration.
- voice -- enables voice agent capabilities.
The default features should be openai only. Write out the complete [features] section
and explain how a consumer would enable ollama and rag but not voice.
Exercise 9.6: Package Publishing Simulation Walk through the complete lifecycle of building and consuming a package:
- Create a package called
string-metricswithneam-pkg init string-metrics --type library. It should expose two public functions: levenshtein(a, b)-- returns the edit distance between two strings (you can use a simplified version that just counts character differences at each position).-
similarity(a, b)-- returns a number between 0.0 and 1.0 representing how similar the strings are (hint: uselevenshteininternally). -
Write the
neam.tomlusing the[package]key with at leastname,version,description,keywords, andcategories. -
Write at least four tests covering normal cases, edge cases (empty strings, nil input), and identical strings.
-
Create a consumer project called
name-matcherwithneam-pkg init name-matcher. In itsneam.toml, addstring-metricsas a dependency. Write amain.neamthat importsstring_metrics.coreand usessimilarity()to find the closest match for a name in a list of candidates:
import string_metrics.core;
{
let target = "Praveen";
let candidates = ["Pravin", "Praven", "Preveen", "Pranav", "Pavan"];
let best_match = nil;
let best_score = 0.0;
for (name in candidates) {
let score = core.similarity(target, name);
if (score > best_score) {
best_score = score;
best_match = name;
}
}
emit "Best match for '" + target + "': " + best_match + " (score: " + str(best_score) + ")";
}
- Verify the full workflow:
neam-pkg checkin the library,neam-pkg installin the consumer, then compile and run.