1010 branches :
1111 - " *"
1212
13+ permissions : {}
14+
1315concurrency :
1416 group : ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
15- cancel-in-progress : false
17+ cancel-in-progress : true
1618
1719jobs :
1820 linting :
19- runs-on : ubuntu-latest
21+ runs-on : blacksmith-2vcpu- ubuntu-2404
2022 steps :
21- - uses : actions/checkout@v3
22- - uses : astral-sh/ruff-action@v3
23+ - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
24+ with :
25+ persist-credentials : false
26+ - name : Install uv and set Python version
27+ uses : astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
28+ with :
29+ version : " 0.11.2"
30+ python-version : " 3.13"
31+ enable-cache : true # zizmor: ignore[cache-poisoning] CI-only, no artifacts published
32+ - name : Install dependencies
33+ run : uv sync --locked
34+ - name : Run Ruff
35+ run : uv run --frozen ruff check .
2336
2437 type-checking :
25- runs-on : ubuntu-latest
38+ runs-on : blacksmith-2vcpu- ubuntu-2404
2639 steps :
27- - uses : actions/checkout@v3
28- - name : Set up Python
29- uses : actions/setup-python@v4
40+ - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
3041 with :
31- python-version : " 3.13"
32- - name : Install poetry
33- uses : abatilo/actions-poetry@v2
34- - name : Setup a local virtual environment
35- run : |
36- poetry config virtualenvs.create true --local
37- poetry config virtualenvs.in-project true --local
38- - uses : actions/cache@v3
39- name : Define a cache for the virtual environment based on the dependencies lock file
42+ persist-credentials : false
43+ - name : Install uv and set Python version
44+ uses : astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
4045 with :
41- path : ./.venv
42- key : venv-type-check-${{ hashFiles('poetry.lock') }}
43- - uses : actions/cache@v3
46+ version : " 0.11.2"
47+ python-version : " 3.13"
48+ enable-cache : true # zizmor: ignore[cache-poisoning] CI-only, no artifacts published
49+ - uses : actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 # zizmor: ignore[cache-poisoning]
4450 name : Cache mypy cache
4551 with :
4652 path : ./.mypy_cache
4753 key : mypy-${{ hashFiles('**/*.py', 'pyproject.toml') }}
4854 restore-keys : |
4955 mypy-
5056 - name : Install dependencies
51- run : poetry install --only=main,dev
57+ run : uv sync --locked
5258 - name : Run mypy type checking
53- run : poetry run mypy langfuse --no-error-summary
59+ run : uv run --frozen mypy langfuse --no-error-summary
5460
55- ci :
56- runs-on : ubuntu-latest
61+ unit-tests :
62+ runs-on : blacksmith-4vcpu- ubuntu-2404
5763 timeout-minutes : 30
5864 env :
5965 LANGFUSE_BASE_URL : " http://localhost:3000"
60- LANGFUSE_PUBLIC_KEY : " pk-lf-1234567890"
61- LANGFUSE_SECRET_KEY : " sk-lf-1234567890"
62- OPENAI_API_KEY : ${{ secrets.OPENAI_API_KEY }}
63- # SERPAPI_API_KEY: ${{ secrets.SERPAPI_API_KEY }}
64- HUGGINGFACEHUB_API_TOKEN : ${{ secrets.HUGGINGFACEHUB_API_TOKEN }}
65- ANTHROPIC_API_KEY : ${{ secrets.ANTHROPIC_API_KEY }}
66+ LANGFUSE_PUBLIC_KEY : " pk-lf-test"
67+ LANGFUSE_SECRET_KEY : " sk-lf-test"
68+ OPENAI_API_KEY : " test-openai-key"
6669 strategy :
6770 fail-fast : false
6871 matrix :
@@ -73,125 +76,182 @@ jobs:
7376 - " 3.13"
7477 - " 3.14"
7578
76- name : Test on Python version ${{ matrix.python-version }}
79+ name : Unit tests on Python ${{ matrix.python-version }}
7780 steps :
78- - uses : actions/checkout@v3
79- - uses : pnpm/action-setup@v3
81+ - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
8082 with :
81- version : 9.5.0
83+ persist-credentials : false
84+ - name : Install uv and set Python version
85+ uses : astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
86+ with :
87+ version : " 0.11.2"
88+ python-version : ${{ matrix.python-version }}
89+ enable-cache : true # zizmor: ignore[cache-poisoning] CI-only, no artifacts published
8290
83- - name : Clone langfuse server
91+ - name : Check Python version
92+ run : python --version
93+
94+ - name : Install the project dependencies
95+ run : uv sync --locked
96+
97+ - name : Run the automated tests
8498 run : |
85- git clone https://github.com/langfuse/langfuse.git ./langfuse-server && echo $(cd ./langfuse-server && git rev-parse HEAD)
99+ python --version
100+ uv run --frozen pytest -n auto --dist worksteal -s -v --log-cli-level=INFO tests/unit
86101
87- - name : Setup node (for langfuse server)
88- uses : actions/setup-node@v3
89- with :
90- node-version : 24
102+ e2e-tests :
103+ runs-on : ubuntu-latest
104+ timeout-minutes : 30
105+ strategy :
106+ fail-fast : false
107+ matrix :
108+ include :
109+ - suite : e2e
110+ job_name : E2E shard 1 tests on Python 3.13
111+ shard_name : shard-1
112+ shard_index : 0
113+ shard_count : 2
114+ - suite : e2e
115+ job_name : E2E shard 2 tests on Python 3.13
116+ shard_name : shard-2
117+ shard_index : 1
118+ shard_count : 2
119+ - suite : live_provider
120+ job_name : E2E live-provider tests on Python 3.13
121+ shard_name : live-provider
122+ env :
123+ LANGFUSE_BASE_URL : " http://localhost:3000"
124+ LANGFUSE_PUBLIC_KEY : " pk-lf-1234567890"
125+ LANGFUSE_SECRET_KEY : " sk-lf-1234567890"
126+ LANGFUSE_INIT_ORG_ID : " 0c6c96f4-0ca0-4f16-92a8-6dd7d7c6a501"
127+ LANGFUSE_INIT_ORG_NAME : " SDK Test Org"
128+ LANGFUSE_INIT_PROJECT_ID : " 7a88fb47-b4e2-43b8-a06c-a5ce950dc53a"
129+ LANGFUSE_INIT_PROJECT_NAME : " SDK Test Project"
130+ LANGFUSE_INIT_PROJECT_PUBLIC_KEY : " pk-lf-1234567890"
131+ LANGFUSE_INIT_PROJECT_SECRET_KEY : " sk-lf-1234567890"
132+ LANGFUSE_INIT_USER_EMAIL : " sdk-tests@langfuse.local"
133+ LANGFUSE_INIT_USER_NAME : " SDK Tests"
134+ LANGFUSE_INIT_USER_PASSWORD : " langfuse-ci-password"
135+ LANGFUSE_E2E_READ_TIMEOUT_SECONDS : " 60"
136+ LANGFUSE_E2E_READ_INTERVAL_SECONDS : " 0.5"
137+ OPENAI_API_KEY : ${{ secrets.OPENAI_API_KEY }}
138+ # SERPAPI_API_KEY: ${{ secrets.SERPAPI_API_KEY }}
139+ HUGGINGFACEHUB_API_TOKEN : ${{ secrets.HUGGINGFACEHUB_API_TOKEN }}
140+ ANTHROPIC_API_KEY : ${{ secrets.ANTHROPIC_API_KEY }}
91141
92- - name : Cache langfuse server dependencies
93- uses : actions/cache@v3
142+ name : ${{ matrix.job_name }}
143+ steps :
144+ - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
145+ with :
146+ persist-credentials : false
147+ - name : Install uv and set Python version
148+ uses : astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
94149 with :
95- path : ./langfuse-server/node_modules
96- key : |
97- langfuse-server-${{ hashFiles('./langfuse-server/package-lock.json') }}
98- langfuse-server-
150+ version : " 0.11.2"
151+ python-version : " 3.13"
152+ enable-cache : true # zizmor: ignore[cache-poisoning] CI-only, no artifacts published
153+ - name : Install the project dependencies
154+ run : uv sync --locked
155+ - name : Check uv Python version
156+ run : uv run --frozen python --version
157+ - name : Prepare langfuse server compose
158+ run : |
159+ mkdir -p ./langfuse-server
160+ LANGFUSE_SERVER_SHA="$(git ls-remote https://github.com/langfuse/langfuse.git HEAD | cut -f1)"
161+ curl -fsSL "https://raw.githubusercontent.com/langfuse/langfuse/${LANGFUSE_SERVER_SHA}/docker-compose.yml" \
162+ -o ./langfuse-server/docker-compose.yml
163+ echo "${LANGFUSE_SERVER_SHA}"
99164
100165 - name : Run langfuse server
101166 run : |
102167 cd ./langfuse-server
103168
104- echo "::group::Run langfuse server"
105- TELEMETRY_ENABLED=false docker compose up -d postgres
106- echo "::endgroup::"
107-
108- echo "::group::Logs from langfuse server"
109- TELEMETRY_ENABLED=false docker compose logs
110- echo "::endgroup::"
111-
112- echo "::group::Install dependencies (necessary to run seeder)"
113- pnpm i
114- echo "::endgroup::"
115-
116- echo "::group::Seed db"
117- cp .env.dev.example .env
118- pnpm run db:migrate
119- pnpm run db:seed
120- echo "::endgroup::"
121- rm -rf .env
122-
123- echo "::group::Run server"
124-
169+ echo "::group::Start langfuse server"
125170 TELEMETRY_ENABLED=false \
171+ NEXT_PUBLIC_LANGFUSE_RUN_NEXT_INIT=true \
126172 LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT=http://localhost:9090 \
127173 LANGFUSE_INGESTION_QUEUE_DELAY_MS=10 \
128174 LANGFUSE_INGESTION_CLICKHOUSE_WRITE_INTERVAL_MS=10 \
175+ LANGFUSE_EXPERIMENT_INSERT_INTO_EVENTS_TABLE=true \
176+ QUEUE_CONSUMER_EVENT_PROPAGATION_QUEUE_IS_ENABLED=true \
177+ LANGFUSE_ENABLE_EVENTS_TABLE_V2_APIS=true \
178+ LANGFUSE_ENABLE_EVENTS_TABLE_OBSERVATIONS=true \
129179 docker compose up -d
130-
131180 echo "::endgroup::"
132181
133- # Add this step to check the health of the container
134182 - name : Health check for langfuse server
135183 run : |
136184 echo "Checking if the langfuse server is up..."
137185 retry_count=0
138- max_retries=10
139- until curl --output /dev/null --silent --head --fail http://localhost:3000/api/public/health
186+ max_retries=20
187+ until curl --output /dev/null --silent --head --fail http://localhost:3000/api/public/health && \
188+ uv run --frozen python -c "from langfuse import Langfuse; client = Langfuse(); project_id = client._get_project_id(); assert project_id == '7a88fb47-b4e2-43b8-a06c-a5ce950dc53a', project_id; print(project_id)"
140189 do
141190 retry_count=`expr $retry_count + 1`
142191 echo "Attempt $retry_count of $max_retries..."
143192 if [ $retry_count -ge $max_retries ]; then
144193 echo "Langfuse server did not respond in time. Printing logs..."
145- docker logs langfuse-server-langfuse-web-1
194+ (cd ./langfuse-server && docker compose ps)
195+ (cd ./langfuse-server && docker compose logs langfuse-web langfuse-worker)
146196 echo "Failing the step..."
147197 exit 1
148198 fi
149199 sleep 5
150200 done
151201 echo "Langfuse server is up and running!"
152202
153- - name : Install Python
154- uses : actions/setup-python@v4
155- # see details (matrix, python-version, python-version-file, etc.)
156- # https://github.com/actions/setup-python
157- with :
158- python-version : ${{ matrix.python-version }}
159-
160- - name : Check python version
161- run : python --version
162-
163- - name : Install poetry
164- uses : abatilo/actions-poetry@v2
165-
166- - name : Set poetry python version
203+ - name : Select e2e shard files
204+ if : ${{ matrix.suite == 'e2e' }}
167205 run : |
168- poetry env use ${{ matrix.python-version }}
169- poetry env info
206+ uv run --frozen python scripts/select_e2e_shard.py \
207+ --shard-index ${{ matrix.shard_index }} \
208+ --shard-count ${{ matrix.shard_count }} \
209+ --json
210+ uv run --frozen python scripts/select_e2e_shard.py \
211+ --shard-index ${{ matrix.shard_index }} \
212+ --shard-count ${{ matrix.shard_count }} \
213+ > "$RUNNER_TEMP/e2e-shard-files.txt"
214+ cat "$RUNNER_TEMP/e2e-shard-files.txt"
170215
171- - name : Setup a local virtual environment (if no poetry.toml file)
216+ - name : Run the parallel end-to-end tests
217+ if : ${{ matrix.suite == 'e2e' }}
172218 run : |
173- poetry config virtualenvs.create true --local
174- poetry config virtualenvs.in-project true --local
175-
176- - uses : actions/cache@v3
177- name : Define a cache for the virtual environment based on the dependencies lock file
178- with :
179- path : ./.venv
180- key : |
181- venv-${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }}-${{ github.sha }}
219+ uv run --frozen python --version
220+ mapfile -t e2e_files < "$RUNNER_TEMP/e2e-shard-files.txt"
221+ set +e
222+ uv run --frozen pytest -n 4 --dist worksteal -s -v --log-cli-level=INFO "${e2e_files[@]}" -m "not serial_e2e"
223+ status=$?
224+ set -e
225+ if [ "$status" -eq 5 ]; then
226+ echo "No parallel e2e tests selected for this shard."
227+ elif [ "$status" -ne 0 ]; then
228+ exit "$status"
229+ fi
182230
183- - name : Install the project dependencies
184- run : poetry install --all-extras
231+ - name : Run serial end-to-end tests
232+ if : ${{ matrix.suite == 'e2e' }}
233+ run : |
234+ mapfile -t e2e_files < "$RUNNER_TEMP/e2e-shard-files.txt"
235+ set +e
236+ uv run --frozen pytest -s -v --log-cli-level=INFO "${e2e_files[@]}" -m "serial_e2e"
237+ status=$?
238+ set -e
239+ if [ "$status" -eq 5 ]; then
240+ echo "No serial e2e tests selected for this shard."
241+ elif [ "$status" -ne 0 ]; then
242+ exit "$status"
243+ fi
185244
186- - name : Run the automated tests
245+ - name : Run live-provider tests
246+ if : ${{ matrix.suite == 'live_provider' }}
187247 run : |
188- python --version
189- poetry run pytest -n auto --dist loadfile -s -v --log-cli-level=INFO
248+ uv run --frozen python --version
249+ uv run --frozen pytest -n 4 --dist worksteal -s -v --log-cli-level=INFO tests/live_provider -m "live_provider"
190250
191251 all-tests-passed :
192252 # This allows us to have a branch protection rule for tests and deploys with matrix
193- runs-on : ubuntu-latest
194- needs : [ci , linting, type-checking]
253+ runs-on : blacksmith-2vcpu- ubuntu-2404
254+ needs : [unit-tests, e2e-tests , linting, type-checking]
195255 if : always()
196256 steps :
197257 - name : Successful deploy
0 commit comments