Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @deepfates
23 changes: 23 additions & 0 deletions .github/ISSUE_TEMPLATE/bug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
name: Bug report
about: Something in Imp behaves differently than documented
labels: bug
---

## What you ran

The smallest program, `mix` command or IEx session that shows it. Include the
signature or module if the behavior depends on it.

## What you expected

## What happened

Paste the error, stacktrace or wrong output verbatim.

## Versions

- Imp (tag, branch or commit):
- Elixir (`elixir --version`):
- OTP:
- Provider, if the failure involves a model call:
6 changes: 3 additions & 3 deletions .github/actions/elixir-setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ outputs:
runs:
using: composite
steps:
- uses: erlef/setup-beam@v1
- uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1
id: beam
with:
otp-version: "28"
elixir-version: "1.19"
- name: Cache Elixir deps and build (single writer)
if: inputs.save-cache == 'true'
uses: actions/cache@v4
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
deps
Expand All @@ -94,7 +94,7 @@ runs:
key: mix-v4-${{ runner.os }}-otp${{ steps.beam.outputs.otp-version }}-elixir${{ steps.beam.outputs.elixir-version }}-${{ hashFiles('mix.lock') }}
- name: Restore Elixir deps and build (read-only)
if: inputs.save-cache != 'true'
uses: actions/cache/restore@v4
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
deps
Expand Down
28 changes: 13 additions & 15 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
## User Story
## What changed

What becomes possible or more reliable for an Imp user?
What becomes possible or more reliable for an Imp user, and what public
behavior changed. Name the upstream source or paper if one is involved.

## Semantic Contract
## How it was verified

Describe upstream fidelity, intentional Elixir-native behavior, and important
invariants.
Run `mix check` — it is the ordinary merge signal and it needs no credentials.
Paste the summary line. Run any further gate your change touches
(`mix protocol.check`, `mix package.check`, `mix quality.check`,
`mix dialyzer.check`); `CONTRIBUTING.md` lists them all.

## Evidence
Say what you could not run and why. Provider-backed, research-scale and
benchmark checks need credentials, datasets or spend: they are the
maintainer's to run, and an outside contributor is not expected to.

- [ ] Deterministic tests
- [ ] Integration or protocol tests where applicable
- [ ] Live/provider evidence where applicable
- [ ] ExDoc, guide, or Livebook updates
- [ ] `mix quality.check`
## Documentation

## External Evidence

List any paper-scale, paid-provider, or external-service evidence and its
reproducible artifact. State clearly when it was not run.
Update ExDoc, guides or Livebooks alongside a public API change.
33 changes: 19 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ concurrency:
# documentation, static analysis, and the pinned upstream differential stay
# separate because they exercise genuinely different environments. Research
# campaigns and evidence receipts are not release gates.
#
# Every third-party action is pinned to a full commit SHA with its version tag
# in a trailing comment: a public repository runs workflows for outside
# contributors, and a moving tag is a moving dependency. Bump the SHA and the
# comment together.

jobs:
changes:
Expand All @@ -30,8 +35,8 @@ jobs:
code: ${{ steps.filter.outputs.code }}
docs: ${{ steps.filter.outputs.docs }}
steps:
- uses: actions/checkout@v7
- uses: dorny/paths-filter@v3
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
id: filter
with:
filters: |
Expand All @@ -56,7 +61,7 @@ jobs:
name: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: ./.github/actions/elixir-setup
Expand All @@ -71,10 +76,10 @@ jobs:
name: stranger.check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: erlef/setup-beam@v1
- uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1
with:
otp-version: "28"
elixir-version: "1.19"
Expand All @@ -88,7 +93,7 @@ jobs:
needs: changes
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
# Single writer of the mix deps/_build/~/.hex cache: this is the job
Expand All @@ -108,7 +113,7 @@ jobs:
needs: changes
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: ./.github/actions/elixir-setup
Expand All @@ -120,7 +125,7 @@ jobs:
needs: changes
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: ./.github/actions/elixir-setup
Expand All @@ -132,7 +137,7 @@ jobs:
needs: changes
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: ./.github/actions/elixir-setup
Expand All @@ -144,7 +149,7 @@ jobs:
needs: changes
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: ./.github/actions/elixir-setup
Expand All @@ -159,7 +164,7 @@ jobs:
# define the result. The updated PLT is then saved under the new exact
# key. This job is the only writer of this cache.
- name: Cache dialyzer PLTs
uses: actions/cache@v4
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: priv/plts
key: plt-v1-${{ runner.os }}-otp${{ steps.setup.outputs.otp-version }}-elixir${{ steps.setup.outputs.elixir-version }}-${{ hashFiles('mix.lock') }}
Expand All @@ -175,11 +180,11 @@ jobs:
needs: changes
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: ./.github/actions/elixir-setup
- uses: actions/setup-python@v5
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
# The :dspy_parity suite validates Imp against real pinned DSPy 3.2.1:
Expand Down Expand Up @@ -222,7 +227,7 @@ jobs:
# Code edits can break doc rendering and livebook validation too.
if: needs.changes.outputs.docs == 'true' || needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: ./.github/actions/elixir-setup
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/evidence.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
name: evidence.venv-free
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: ./.github/actions/elixir-setup
Expand All @@ -49,21 +49,21 @@ jobs:
if: inputs.full
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: ./.github/actions/elixir-setup
with:
save-cache: "false"
- uses: actions/setup-python@v6
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
cache: "pip"
cache-dependency-path: |
scripts/setup_dspy_parity_env.sh
scripts/setup_dspy_current_target.sh
scripts/setup_reference_test_env.sh
- uses: denoland/setup-deno@v2
- uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5
with:
deno-version: v2.8.3
- name: Check out the pinned DSPy 3.2.1 source tree
Expand Down
41 changes: 20 additions & 21 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Working on Imp

Read this before changing anything here. The workshop's `AGENTS.md` has the
idioms these repositories share; this file is what is specific to Imp.
Orientation for anyone — person or agent — changing this repository.
`CONTRIBUTING.md` has the gates and the development setup; `decisions.md` has
the rulings that are in force and the condition under which each retires. This
file is the design context those two assume.

## What Imp owns

Expand All @@ -12,10 +14,10 @@ application rather than owning one.

## What Imp does not own

Product-specific characters, residents, accounts, inboxes, or chat threads.
Imp now includes the optional `Imp.ACP` program adapter and the generic
`Imp.MCP` tool integration. ExMCP owns both wire protocols. Imp owns typed
program/tool conversion and execution; Dwell and hosts own product lifetimes.
Product-specific characters, accounts, inboxes, or chat threads. Imp includes
the optional `Imp.ACP` program adapter and the generic `Imp.MCP` tool
integration; ExMCP owns both wire protocols. Imp owns typed program/tool
conversion and execution, and the host application owns product lifetimes.
Ordinary Imp startup opens no protocol listeners or remote connections.

## The centre of the model
Expand All @@ -26,13 +28,10 @@ around it, and persistence stores it as plain data. Thirty-three modules read it
When something needs to know a program's shape, it should ask the signature
rather than re-describe it.

This is the strongest instance of "declare once, derive everything" in the
constellation, and it is the pattern the other repositories are measured against.

## Where the boundaries are half-declared

Imp has more `@callback` boundaries than any other repository here — and nearly
all of them return `{:error, term()}`. Some paths return bare strings
Imp has many `@callback` boundaries, and nearly all of them return
`{:error, term()}`. Some paths return bare strings
(`{:error, "expected an LM module exporting generate/2"}`), which a caller cannot
act on except by matching text.

Expand All @@ -49,10 +48,9 @@ process-group lifecycle on raw ports: TERM, grace, KILL, and a check that the
group is gone. The grace period is the requirement worth preserving — a trainer
SIGKILLed mid-checkpoint loses work — and it is the reason Imp cannot simply
adopt `ExMCP.Internal.OwnedProcess`, which stops the root process before
signalling and so can never deliver a handleable TERM.

If that consolidation happens, graceful shutdown is a precondition, not a
follow-up. Until then this is a documented divergence rather than an accident.
signalling and so can never deliver a handleable TERM. If that consolidation
happens, graceful shutdown is a precondition, not a follow-up. Until then this is
a documented divergence rather than an accident.

## Protocol integration

Expand All @@ -63,12 +61,13 @@ adds ACP presentation hints to that import; it is not another client.
The shared ExMCP pin and its fork reasons live in `mix.exs`.

`Imp.ACP` owns the default session/program adapter formerly shipped separately
as imp_acp. Its namespace stays stable, but consumers depend on Imp directly.
`docs/PRODUCTION_OPERATIONS.md` describes the MCP lifecycle/API migration.
as the `imp_acp` package. Its namespace stays stable, but consumers depend on
Imp directly. `docs/PRODUCTION_OPERATIONS.md` describes the MCP lifecycle and
API migration.

## Checks

`CONTRIBUTING.md` lists the gates. `mix check` is the default; provider-backed,
research-scale and evidence-infrastructure runs are deliberately separate
because they need credentials, datasets or spend. Keep that separation — a green
default run is not evidence about a fidelity claim.
`mix check` is the default merge signal and needs no credentials. Provider-backed
and research-scale runs are deliberately separate because they need credentials,
datasets or spend. Keep that separation — a green default run is not evidence
about a fidelity claim.
19 changes: 19 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# NOTICE

Imp is licensed under the MIT License; see `LICENSE`.

Imp contains code ported from DSPy (https://github.com/stanfordnlp/dspy),
which is licensed under the MIT License:

Copyright (c) 2023 Stanford Future Data Systems

The following files are ports of DSPy source and carry that copyright
alongside Imp's own:

- `lib/imp/optimizer/utils.ex` — ported from `dspy/teleprompt/utils.py` and
`dspy/teleprompt/bootstrap_trace.py`.
- `lib/imp/adapter/chat.ex` — its field-marker section parsing is ported line
for line from DSPy's chat adapter.

Other modules name DSPy concepts or reproduce its semantics without copying
its source; those are independent implementations.
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ rather than hand-editing prompts—to the BEAM.
integrated protocol adapters — `Imp.ACP` and `Imp.MCP.connect/2`, absorbed from
the retired `imp_acp` package — exist only on `main` until the next tag.
Installing `v0.3.2` gives you the library described below without them; using
the adapters today means a local checkout of `main`.
the adapters today means depending on `main` (see [Install](#install)).

Here, “typed” means required inputs are checked and model outputs are parsed
and validated against the signature before application code receives them.
Expand Down Expand Up @@ -131,13 +131,24 @@ decision.

## Install

Imp is not published to Hex. Install the private source release from its
immutable tag (GitHub credentials with access to the repository are required):
Imp is not published to Hex. It is installed from this public repository, at
an immutable tag, with no credentials:

```elixir
{:imp, github: "deepfates/imp", tag: "v0.3.2"}
```

That tag does not contain `Imp.ACP` or `Imp.MCP.connect/2`; a tag that does
will be cut. Until then, depend on `{:imp, github: "deepfates/imp", branch:
"main"}` if you need the adapters.

ExMCP is declared `runtime: false`, so an OTP release that uses `Imp.ACP` or
`Imp.MCP` must list `applications: [ex_mcp: :load]` in its release definition;
see [protocol runtime in releases](docs/PRODUCTION_OPERATIONS.md#protocol-runtime-in-releases).

Imp is MIT licensed (`LICENSE`); `NOTICE` records the upstream DSPy code two
modules are ported from.

Use `{:imp, path: "path/to/imp"}` only while developing against a local
checkout. Imp requires Elixir `~> 1.19`. Commit your application's `mix.lock`;
the Git tag fixes Imp's source, while normal Mix constraints may otherwise
Expand Down
4 changes: 2 additions & 2 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ selected program under OTP.

## Install

`v0.3.2` is a private Git source release. GitHub credentials with access to the
repository are required.
`v0.3.2` is a Git source release from a public repository; no credentials are
required.

```elixir
{:imp, github: "deepfates/imp", tag: "v0.3.2"}
Expand Down
Loading
Loading