Skip to content
Open
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
24 changes: 17 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,22 +93,32 @@ Common contribution types include: `doc`, `code`, `bug`, and `ideas`. See the fu

## Development environment

We use the ["scripts to rule them all"](https://github.blog/engineering/engineering-principles/scripts-to-rule-them-all/) philosophy to manage common tasks across the project. These are mostly backed by a Makefile that contains the implementation.

You'll need the following dependencies installed to build Cog locally:
- [Go](https://golang.org/doc/install): We're targeting 1.24, but you can install the latest version since Go is backwards compatible. If you're using a newer Mac with an M1 chip, be sure to download the `darwin-arm64` installer package. Alternatively you can run `brew install go` which will automatically detect and use the appropriate installer for your system architecture.
- [uv](https://docs.astral.sh/uv/): Python versions and dependencies are managed by uv.

- [Go](https://golang.org/doc/install): We're targeting 1.23, but you can install the latest version since Go is backwards compatible. If you're using a newer Mac with an M1 chip, be sure to download the `darwin-arm64` installer package. Alternatively you can run `brew install go` which will automatically detect and use the appropriate installer for your system architecture.
- [uv](https://docs.astral.sh/uv/): Python versions and dependencies are managed by uv, both in development and container environments.
- [Docker](https://docs.docker.com/desktop) or [OrbStack](https://orbstack.dev)

Install the Python dependencies:

script/setup

Once you have Go installed you can install the cog binary by running:
Once you have Go installed, run:

make install

This will build and install the `cog` binary to `/usr/local/bin/cog`. You can then use it to build and run models.

## Package Management

Cog uses [uv](https://docs.astral.sh/uv/) for Python package management, both in development and container environments. This provides:

make install PREFIX=$(go env GOPATH)
- Fast, reliable package installation
- Consistent dependency resolution
- Efficient caching
- Reproducible builds

This installs the `cog` binary to `$GOPATH/bin/cog`.
When building containers, uv is automatically installed and used to install Python packages from requirements.txt files. The cache is mounted at `/srv/r8/uv/cache` to speed up subsequent builds.

To run ALL the tests:

Expand Down
31 changes: 15 additions & 16 deletions pkg/dockerfile/standard_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,18 +414,17 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked apt-get update -qq &
git \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
` + fmt.Sprintf(`

ENV UV_CACHE_DIR="/srv/r8/uv/cache"
RUN --mount=type=cache,target=/root/.cache/pip curl -s -S -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash && \
git clone https://github.com/momo-lab/pyenv-install-latest.git "$(pyenv root)"/plugins/pyenv-install-latest && \
export PYTHON_CONFIGURE_OPTS='--enable-optimizations --with-lto' && \
export PYTHON_CFLAGS='-O3' && \
pyenv install-latest "%s" && \
pyenv global $(pyenv install-latest --print "%s") && \
pip install "wheel<1"`, py, py) + `
pyenv install-latest "` + py + `" && \
pyenv global $(pyenv install-latest --print "` + py + `") && \
curl -LsSf https://astral.sh/uv/install.sh | sh

RUN rm -rf /usr/bin/python3 && ln -s ` + "`realpath \\`pyenv which python\\`` /usr/bin/python3 && chmod +x /usr/bin/python3", nil
// for sitePackagesLocation, kind of need to determine which specific version latest is (3.8 -> 3.8.17 or 3.8.18)
// install-latest essentially does pyenv install --list | grep $py | tail -1
// there are many bad options, but a symlink to $(pyenv prefix) is the least bad one
}

func (g *StandardGenerator) installCog() (string, error) {
Expand All @@ -451,7 +450,7 @@ func (g *StandardGenerator) installCog() (string, error) {
cmds := []string{
"ENV R8_COG_VERSION=coglet",
"ENV R8_PYTHON_VERSION=" + g.Config.Build.PythonVersion,
"RUN pip install " + m.LatestCoglet.URL,
"RUN --mount=type=cache,target=/srv/r8/uv/cache,id=uv-cache uv pip install " + m.LatestCoglet.URL,
}
return strings.Join(cmds, "\n"), nil
}
Expand All @@ -469,13 +468,13 @@ func (g *StandardGenerator) installCog() (string, error) {
if err != nil {
return "", err
}
pipInstallLine := "RUN --mount=type=cache,target=/root/.cache/pip pip install --no-cache-dir"
pipInstallLine += " " + containerPath
pipInstallLine += " 'pydantic>=1.9,<3'"
uvInstallLine := "RUN --mount=type=cache,target=/srv/r8/uv/cache,id=uv-cache uv pip install --no-cache-dir"
uvInstallLine += " " + containerPath
uvInstallLine += " 'pydantic>=1.9,<3'"
if g.strip {
pipInstallLine += " && " + StripDebugSymbolsCommand
uvInstallLine += " && " + StripDebugSymbolsCommand
}
lines = append(lines, CFlags, pipInstallLine, "ENV CFLAGS=")
lines = append(lines, CFlags, uvInstallLine, "ENV CFLAGS=")
return strings.Join(lines, "\n"), nil
}

Expand Down Expand Up @@ -509,14 +508,14 @@ func (g *StandardGenerator) pipInstalls() (string, error) {
return "", err
}

pipInstallLine := "RUN --mount=type=cache,target=/root/.cache/pip pip install -r " + containerPath
uvInstallLine := "RUN --mount=type=cache,target=/srv/r8/uv/cache,id=uv-cache uv pip install -r " + containerPath
if g.strip {
pipInstallLine += " && " + StripDebugSymbolsCommand
uvInstallLine += " && " + StripDebugSymbolsCommand
}
return strings.Join([]string{
copyLine[0],
CFlags,
pipInstallLine,
uvInstallLine,
"ENV CFLAGS=",
}, "\n"), nil
}
Expand Down
61 changes: 58 additions & 3 deletions pkg/dockerfile/standard_generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func testInstallCog(relativeTmpDir string, stripped bool) string {
}
return fmt.Sprintf(`COPY %s/%s /tmp/%s
ENV CFLAGS="-O3 -funroll-loops -fno-strict-aliasing -flto -S"
RUN --mount=type=cache,target=/root/.cache/pip pip install --no-cache-dir /tmp/%s 'pydantic>=1.9,<3'%s
RUN --mount=type=cache,target=/srv/r8/uv/cache,id=uv-cache uv pip install --no-cache-dir /tmp/%s 'pydantic>=1.9,<3'%s
ENV CFLAGS=`, relativeTmpDir, wheel, wheel, wheel, strippedCall)
}

Expand All @@ -73,13 +73,15 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked apt-get update -qq &
git \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

ENV UV_CACHE_DIR="/srv/r8/uv/cache"
RUN --mount=type=cache,target=/root/.cache/pip curl -s -S -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash && \
git clone https://github.com/momo-lab/pyenv-install-latest.git "$(pyenv root)"/plugins/pyenv-install-latest && \
export PYTHON_CONFIGURE_OPTS='--enable-optimizations --with-lto' && \
export PYTHON_CFLAGS='-O3' && \
pyenv install-latest "%s" && \
pyenv global $(pyenv install-latest --print "%s") && \
pip install "wheel<1"
curl -LsSf https://astral.sh/uv/install.sh | sh
`, version, version)
}

Expand Down Expand Up @@ -414,7 +416,7 @@ ENV NVIDIA_DRIVER_CAPABILITIES=all
` + testInstallPython("3.12") + `RUN rm -rf /usr/bin/python3 && ln -s ` + "`realpath \\`pyenv which python\\`` /usr/bin/python3 && chmod +x /usr/bin/python3" + `
COPY ` + gen.relativeTmpDir + `/requirements.txt /tmp/requirements.txt
ENV CFLAGS="-O3 -funroll-loops -fno-strict-aliasing -flto -S"
RUN --mount=type=cache,target=/root/.cache/pip pip install -r /tmp/requirements.txt
RUN --mount=type=cache,target=/srv/r8/uv/cache,id=uv-cache uv pip install -r /tmp/requirements.txt
ENV CFLAGS=
` + testInstallCog(gen.relativeTmpDir, gen.strip) + `
RUN find / -type f -name "*python*.so" -printf "%h\n" | sort -u > /etc/ld.so.conf.d/cog.conf && ldconfig
Expand Down Expand Up @@ -898,3 +900,56 @@ torch==2.3.1
pandas==2.0.3
coglet @ https://github.com/replicate/cog-runtime/releases/download/v0.1.0-alpha31/coglet-0.1.0a31-py3-none-any.whl`, string(requirements))
}

func TestGenerateDockerfileStripped(t *testing.T) {
tmpDir := t.TempDir()

conf, err := config.FromYAML([]byte(`
build:
gpu: true
cuda: "11.8"
python_version: "3.12"
system_packages:
- ffmpeg
- cowsay
python_packages:
- torch==2.3.1
- pandas==2.0.3
run:
- "cowsay moo"
predict: predict.py:Predictor
`))
require.NoError(t, err)
require.NoError(t, conf.ValidateAndComplete(""))
command := dockertest.NewMockCommand()
client := registrytest.NewMockRegistryClient()
gen, err := NewStandardGenerator(conf, tmpDir, command, client, true)
require.NoError(t, err)
gen.SetUseCogBaseImage(true)
gen.SetStrip(true)
_, actual, _, err := gen.GenerateModelBaseWithSeparateWeights(t.Context(), "r8.im/replicate/cog-test")
require.NoError(t, err)

expected := `#syntax=docker/dockerfile:1.4
FROM r8.im/replicate/cog-test-weights AS weights
FROM r8.im/cog-base:cuda11.8-python3.12-torch2.3.1
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked apt-get update -qq && apt-get install -qqy cowsay && rm -rf /var/lib/apt/lists/*
COPY ` + gen.relativeTmpDir + `/requirements.txt /tmp/requirements.txt
ENV CFLAGS="-O3 -funroll-loops -fno-strict-aliasing -flto -S"
RUN --mount=type=cache,target=/srv/r8/uv/cache,id=uv-cache uv pip install -r /tmp/requirements.txt && find / -type f -name "*python*.so" -not -name "*cpython*.so" -exec strip -S {} \;
ENV CFLAGS=
RUN find / -type f -name "*.py[co]" -delete && find / -type f -name "*.py" -exec touch -t 197001010000 {} \; && find / -type f -name "*.py" -printf "%h\n" | sort -u | /usr/bin/python3 -m compileall --invalidation-mode timestamp -o 2 -j 0
RUN cowsay moo
WORKDIR /src
EXPOSE 5000
CMD ["python", "-m", "cog.server.http"]
COPY . /src`

require.Equal(t, expected, actual)

requirements, err := os.ReadFile(path.Join(gen.tmpDir, "requirements.txt"))
require.NoError(t, err)
require.Equal(t, `--extra-index-url https://download.pytorch.org/whl/cu118
torch==2.3.1
pandas==2.0.3`, string(requirements))
}