diff --git a/.github/workflows/deploy-docs-site.yaml b/.github/workflows/deploy-docs-site.yaml index d3fee6cb..721a5b30 100644 --- a/.github/workflows/deploy-docs-site.yaml +++ b/.github/workflows/deploy-docs-site.yaml @@ -8,6 +8,9 @@ on: - ".github/workflows/deploy-docs-site.yaml" - "docs/**" pull_request: + paths: + - ".github/workflows/deploy-docs-site.yaml" + - "docs/**" jobs: deploy: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index bb844a34..1bf0ac6a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -29,6 +29,15 @@ jobs: deploy-dev: needs: [build-backend, build-web, build-bot] runs-on: ubuntu-latest + env: + WEB_ADMIN_USERS: ${{ secrets.DEV_WEB_ADMIN_USERS }} + WEB_DISCORD_CLIENT_ID: ${{ secrets.DEV_WEB_DISCORD_CLIENT_ID }} + WEB_DISCORD_CLIENT_SECRET: ${{ secrets.DEV_WEB_DISCORD_CLIENT_SECRET }} + WEB_EMAIL_SERVER_HOST: ${{ secrets.DEV_WEB_EMAIL_SERVER_HOST }} + WEB_EMAIL_SERVER_PASSWORD: ${{ secrets.DEV_WEB_EMAIL_SERVER_PASSWORD }} + WEB_EMAIL_SERVER_PORT: ${{ secrets.DEV_WEB_EMAIL_SERVER_PORT }} + WEB_EMAIL_SERVER_USER: ${{ secrets.DEV_WEB_EMAIL_SERVER_USER }} + WEB_NEXTAUTH_SECRET: ${{ secrets.DEV_WEB_NEXTAUTH_SECRET }} steps: - name: Checkout uses: actions/checkout@v2 diff --git a/.github/workflows/test-e2e.yaml b/.github/workflows/test-e2e.yaml new file mode 100644 index 00000000..44ef5b04 --- /dev/null +++ b/.github/workflows/test-e2e.yaml @@ -0,0 +1,39 @@ +name: E2E Tests (Website) + +on: + push: + branches: + - main + paths: + - oasst-shared/** + - backend/** + - website/** + pull_request: + paths: + - oasst-shared/** + - backend/** + - website/** + +jobs: + test-e2e: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Start website, backend, etc + run: docker compose up ci --build -d + - name: Run Cypress tests + uses: cypress-io/github-action@v5.0.2 + with: + browser: chrome + working-directory: website + - uses: actions/upload-artifact@v3 + if: failure() # NOTE: screenshots will be generated only if E2E test failed + with: + name: cypress-screenshots + path: website/cypress/screenshots + - uses: actions/upload-artifact@v3 + if: always() + with: + name: cypress-videos + path: website/cypress/videos diff --git a/ansible/README.md b/ansible/README.md new file mode 100644 index 00000000..2ab1943e --- /dev/null +++ b/ansible/README.md @@ -0,0 +1,7 @@ +To test the ansible playbook on localhost run +`ansible-playbook -i test.inventory.ini dev.yaml`.\ +In case you're missing the ansible docker depencency install it with `ansible-galaxy collection install community.docker`.\ +Point Redis Insights to the Redis database by visiting localhost:8001 in a +browser and select "I already have a database" followed by "Connect to a Redis +Database".\ +For host, port and name fill in `oasst-redis`, `6379` and `redis`. diff --git a/ansible/dev.yaml b/ansible/dev.yaml index 577abd68..6f93aaa6 100644 --- a/ansible/dev.yaml +++ b/ansible/dev.yaml @@ -10,6 +10,39 @@ state: present driver: bridge + - name: Copy redis.conf to managed node + ansible.builtin.copy: + src: ./redis.conf + dest: ./redis.conf + + - name: Set up Redis + community.docker.docker_container: + name: oasst-redis + image: redis + state: started + restart_policy: always + network_mode: oasst + ports: + - 6379:6379 + healthcheck: + test: ["CMD-SHELL", "redis-cli ping | grep PONG"] + interval: 2s + timeout: 2s + retries: 10 + command: redis-server /usr/local/etc/redis/redis.conf + volumes: + - "./redis.conf:/usr/local/etc/redis/redis.conf" + + - name: Set up Redis Insights + community.docker.docker_container: + name: oasst-redis-insights + image: redislabs/redisinsight:latest + state: started + restart_policy: always + network_mode: oasst + ports: + - 8001:8001 + - name: Create postgres containers community.docker.docker_container: name: "{{ item.name }}" @@ -32,14 +65,6 @@ - name: oasst-postgres - name: oasst-postgres-web - - name: Set up maildev - community.docker.docker_container: - name: oasst-maildev - image: maildev/maildev - state: started - restart_policy: always - network_mode: oasst - - name: Run the oasst oasst-backend community.docker.docker_container: name: oasst-backend @@ -51,10 +76,12 @@ network_mode: oasst env: POSTGRES_HOST: oasst-postgres + REDIS_HOST: oasst-redis DEBUG_ALLOW_ANY_API_KEY: "true" DEBUG_USE_SEED_DATA: "true" MAX_WORKERS: "1" RATE_LIMIT: "false" + DEBUG_SKIP_EMBEDDING_COMPUTATION: "true" ports: - 8080:8080 @@ -68,15 +95,27 @@ restart_policy: always network_mode: oasst env: - FASTAPI_URL: http://oasst-backend:8080 - FASTAPI_KEY: "123" + ADMIN_USERS: "{{ lookup('ansible.builtin.env', 'WEB_ADMIN_USERS') }}" DATABASE_URL: postgres://postgres:postgres@oasst-postgres-web/postgres - NEXTAUTH_SECRET: O/M2uIbGj+lDD2oyNa8ax4jEOJqCPJzO53UbWShmq98= - EMAIL_SERVER_HOST: oasst-maildev - EMAIL_SERVER_PORT: "25" - EMAIL_FROM: info@example.com - NEXTAUTH_URL: http://web.dev.open-assistant.io/ DEBUG_LOGIN: "true" + DISCORD_CLIENT_ID: + "{{ lookup('ansible.builtin.env', 'WEB_DISCORD_CLIENT_ID') }}" + DISCORD_CLIENT_SECRET: + "{{ lookup('ansible.builtin.env', 'WEB_DISCORD_CLIENT_SECRET') }}" + EMAIL_FROM: open-assistent@laion.ai + EMAIL_SERVER_HOST: + "{{ lookup('ansible.builtin.env', 'WEB_EMAIL_SERVER_HOST') }}" + EMAIL_SERVER_PASSWORD: + "{{ lookup('ansible.builtin.env', 'WEB_EMAIL_SERVER_PASSWORD') }}" + EMAIL_SERVER_PORT: + "{{ lookup('ansible.builtin.env', 'WEB_EMAIL_SERVER_PORT') }}" + EMAIL_SERVER_USER: + "{{ lookup('ansible.builtin.env', 'WEB_EMAIL_SERVER_USER') }}" + FASTAPI_URL: http://oasst-backend:8080 + FASTAPI_KEY: "1234" + NEXTAUTH_SECRET: + "{{ lookup('ansible.builtin.env', 'WEB_NEXTAUTH_SECRET') }}" + NEXTAUTH_URL: http://web.dev.open-assistant.io/ ports: - 3000:3000 command: bash wait-for-postgres.sh node server.js diff --git a/ansible/redis.conf b/ansible/redis.conf new file mode 100644 index 00000000..58da1e05 --- /dev/null +++ b/ansible/redis.conf @@ -0,0 +1,2 @@ +maxmemory 100mb +maxmemory-policy allkeys-lru diff --git a/ansible/test.inventory.ini b/ansible/test.inventory.ini new file mode 100644 index 00000000..bfe6d93f --- /dev/null +++ b/ansible/test.inventory.ini @@ -0,0 +1,2 @@ +[test] +dev ansible_connection=local diff --git a/backend/alembic/versions/2023_01_08_1106-3d96bb92e33a_added_minilm_embedding_column_to_message.py b/backend/alembic/versions/2023_01_08_1106-3d96bb92e33a_added_minilm_embedding_column_to_message.py new file mode 100644 index 00000000..9b81105f --- /dev/null +++ b/backend/alembic/versions/2023_01_08_1106-3d96bb92e33a_added_minilm_embedding_column_to_message.py @@ -0,0 +1,27 @@ +"""added miniLM_embedding column to message + +Revision ID: 023548d474f7 +Revises: ba61fe17fb6e +Create Date: 2023-01-08 11:06:25.613290 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "023548d474f7" +down_revision = "ba61fe17fb6e" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column("message", sa.Column("miniLM_embedding", sa.ARRAY(sa.Float()), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("message", "miniLM_embedding") + # ### end Alembic commands ### diff --git a/backend/alembic/versions/2023_01_08_1603-35bdc1a08bb8_embedding_for_message_now_in_its_own_.py b/backend/alembic/versions/2023_01_08_1603-35bdc1a08bb8_embedding_for_message_now_in_its_own_.py new file mode 100644 index 00000000..b732b792 --- /dev/null +++ b/backend/alembic/versions/2023_01_08_1603-35bdc1a08bb8_embedding_for_message_now_in_its_own_.py @@ -0,0 +1,49 @@ +"""embedding for message now in its own table + +Revision ID: 35bdc1a08bb8 +Revises: 023548d474f7 +Create Date: 2023-01-08 16:03:48.454207 + +""" +import sqlalchemy as sa +import sqlmodel +from alembic import op +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = "35bdc1a08bb8" +down_revision = "023548d474f7" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "message_embedding", + sa.Column("message_id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("embedding", sa.ARRAY(sa.Float()), nullable=True), + sa.Column("model", sqlmodel.sql.sqltypes.AutoString(length=256), nullable=False), + sa.ForeignKeyConstraint( + ["message_id"], + ["message.id"], + ), + sa.PrimaryKeyConstraint("message_id", "model"), + ) + op.drop_column("message", "miniLM_embedding") + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "message", + sa.Column( + "miniLM_embedding", + postgresql.ARRAY(postgresql.DOUBLE_PRECISION(precision=53)), + autoincrement=False, + nullable=True, + ), + ) + op.drop_table("message_embedding") + # ### end Alembic commands ### diff --git a/backend/alembic/versions/2023_01_08_2128-aac6b2f66006_created_date.py b/backend/alembic/versions/2023_01_08_2128-aac6b2f66006_created_date.py new file mode 100644 index 00000000..6d40d896 --- /dev/null +++ b/backend/alembic/versions/2023_01_08_2128-aac6b2f66006_created_date.py @@ -0,0 +1,30 @@ +"""Created date + +Revision ID: aac6b2f66006 +Revises: 35bdc1a08bb8 +Create Date: 2023-01-08 21:28:27.342729 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "aac6b2f66006" +down_revision = "35bdc1a08bb8" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "message_embedding", + sa.Column("created_date", sa.DateTime(), server_default=sa.text("CURRENT_TIMESTAMP"), nullable=False), + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("message_embedding", "created_date") + # ### end Alembic commands ### diff --git a/backend/oasst_backend/api/v1/hugging_face.py b/backend/oasst_backend/api/v1/hugging_face.py index 1e7f1ffe..62d2ea6b 100644 --- a/backend/oasst_backend/api/v1/hugging_face.py +++ b/backend/oasst_backend/api/v1/hugging_face.py @@ -1,19 +1,14 @@ -from enum import Enum from typing import List from fastapi import APIRouter, Depends from oasst_backend.api import deps from oasst_backend.models import ApiClient from oasst_backend.schemas.hugging_face import ToxicityClassification -from oasst_backend.utils.hugging_face import HuggingFaceAPI +from oasst_backend.utils.hugging_face import HfUrl, HuggingFaceAPI router = APIRouter() -class HF_url(str, Enum): - HUGGINGFACE_TOXIC_ROBERTA = "https://api-inference.huggingface.co/models/unitary/multilingual-toxic-xlm-roberta" - - @router.get("/text_toxicity") async def get_text_toxicity( msg: str, @@ -30,7 +25,7 @@ async def get_text_toxicity( ToxicityClassification: the score of toxicity of the message. """ - api_url: str = HF_url.HUGGINGFACE_TOXIC_ROBERTA.value + api_url: str = HfUrl.HUGGINGFACE_TOXIC_ROBERTA.value hugging_face_api = HuggingFaceAPI(api_url) response = await hugging_face_api.post(msg) diff --git a/backend/oasst_backend/api/v1/tasks.py b/backend/oasst_backend/api/v1/tasks.py index eb10dc00..821ba562 100644 --- a/backend/oasst_backend/api/v1/tasks.py +++ b/backend/oasst_backend/api/v1/tasks.py @@ -7,7 +7,9 @@ from fastapi.security.api_key import APIKey from loguru import logger from oasst_backend.api import deps from oasst_backend.api.v1.utils import prepare_conversation +from oasst_backend.config import settings from oasst_backend.prompt_repository import PromptRepository, TaskRepository +from oasst_backend.utils.hugging_face import HfEmbeddingModel, HfUrl, HuggingFaceAPI from oasst_shared.exceptions import OasstError, OasstErrorCode from oasst_shared.schemas import protocol as protocol_schema from sqlmodel import Session @@ -253,7 +255,7 @@ def tasks_acknowledge_failure( @router.post("/interaction", response_model=protocol_schema.TaskDone) -def tasks_interaction( +async def tasks_interaction( *, db: Session = Depends(deps.get_db), api_key: APIKey = Depends(deps.get_api_key), @@ -274,12 +276,26 @@ def tasks_interaction( ) # here we store the text reply in the database - pr.store_text_reply( + newMessage = pr.store_text_reply( text=interaction.text, frontend_message_id=interaction.message_id, user_frontend_message_id=interaction.user_message_id, ) + if not settings.DEBUG_SKIP_EMBEDDING_COMPUTATION: + try: + hugging_face_api = HuggingFaceAPI( + f"{HfUrl.HUGGINGFACE_FEATURE_EXTRACTION.value}/{HfEmbeddingModel.MINILM.value}" + ) + embedding = await hugging_face_api.post(interaction.text) + pr.insert_message_embedding( + message_id=newMessage.id, model=HfEmbeddingModel.MINILM.value, embedding=embedding + ) + except OasstError: + logger.error( + f"Could not fetch embbeddings for text reply to {interaction.message_id=} with {interaction.text=} by {interaction.user=}." + ) + return protocol_schema.TaskDone() case protocol_schema.MessageRating: logger.info( diff --git a/backend/oasst_backend/config.py b/backend/oasst_backend/config.py index 1765af7a..ed394412 100644 --- a/backend/oasst_backend/config.py +++ b/backend/oasst_backend/config.py @@ -25,6 +25,7 @@ class Settings(BaseSettings): DEBUG_USE_SEED_DATA_PATH: Optional[FilePath] = ( Path(__file__).parent.parent / "test_data/generic/test_generic_data.json" ) + DEBUG_SKIP_EMBEDDING_COMPUTATION: bool = False HUGGING_FACE_API_KEY: str = "" diff --git a/backend/oasst_backend/models/__init__.py b/backend/oasst_backend/models/__init__.py index a856b155..0873381c 100644 --- a/backend/oasst_backend/models/__init__.py +++ b/backend/oasst_backend/models/__init__.py @@ -1,6 +1,7 @@ from .api_client import ApiClient from .journal import Journal, JournalIntegration from .message import Message +from .message_embedding import MessageEmbedding from .message_reaction import MessageReaction from .message_tree_state import MessageTreeState from .task import Task @@ -13,6 +14,7 @@ __all__ = [ "User", "UserStats", "Message", + "MessageEmbedding", "MessageReaction", "MessageTreeState", "Task", diff --git a/backend/oasst_backend/models/message_embedding.py b/backend/oasst_backend/models/message_embedding.py new file mode 100644 index 00000000..74da5004 --- /dev/null +++ b/backend/oasst_backend/models/message_embedding.py @@ -0,0 +1,21 @@ +from datetime import datetime +from typing import List, Optional +from uuid import UUID + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from sqlmodel import ARRAY, Field, Float, SQLModel + + +class MessageEmbedding(SQLModel, table=True): + __tablename__ = "message_embedding" + __table_args__ = (sa.PrimaryKeyConstraint("message_id", "model"),) + + message_id: UUID = Field(sa_column=sa.Column(pg.UUID(as_uuid=True), sa.ForeignKey("message.id"), nullable=False)) + model: str = Field(max_length=256, nullable=False) + embedding: List[float] = Field(sa_column=sa.Column(ARRAY(Float)), nullable=True) + + # In the case that the Message Embedding is created afterwards + created_date: Optional[datetime] = Field( + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) + ) diff --git a/backend/oasst_backend/models/task.py b/backend/oasst_backend/models/task.py index a980c1b5..d923be97 100644 --- a/backend/oasst_backend/models/task.py +++ b/backend/oasst_backend/models/task.py @@ -35,4 +35,4 @@ class Task(SQLModel, table=True): @property def expired(self) -> bool: - return self.expiry_date is not None and datetime.utcnow() < self.expiry_date + return self.expiry_date is not None and datetime.utcnow() > self.expiry_date diff --git a/backend/oasst_backend/prompt_repository.py b/backend/oasst_backend/prompt_repository.py index 7446ec07..c31c0061 100644 --- a/backend/oasst_backend/prompt_repository.py +++ b/backend/oasst_backend/prompt_repository.py @@ -2,13 +2,13 @@ import datetime import random from collections import defaultdict from http import HTTPStatus -from typing import Optional +from typing import List, Optional from uuid import UUID, uuid4 import oasst_backend.models.db_payload as db_payload from loguru import logger from oasst_backend.journal_writer import JournalWriter -from oasst_backend.models import ApiClient, Message, MessageReaction, TextLabels, User +from oasst_backend.models import ApiClient, Message, MessageEmbedding, MessageReaction, TextLabels, User from oasst_backend.models.payload_column_type import PayloadContainer from oasst_backend.task_repository import TaskRepository, validate_frontend_message_id from oasst_backend.user_repository import UserRepository @@ -91,7 +91,12 @@ class PromptRepository: self.db.refresh(message) return message - def store_text_reply(self, text: str, frontend_message_id: str, user_frontend_message_id: str) -> Message: + def store_text_reply( + self, + text: str, + frontend_message_id: str, + user_frontend_message_id: str, + ) -> Message: validate_frontend_message_id(frontend_message_id) validate_frontend_message_id(user_frontend_message_id) @@ -224,6 +229,30 @@ class PromptRepository: OasstErrorCode.TASK_PAYLOAD_TYPE_MISMATCH, ) + def insert_message_embedding(self, message_id: UUID, model: str, embedding: List[float]) -> MessageEmbedding: + """Insert the embedding of a new message in the database. + + Args: + message_id (UUID): the identifier of the message we want to save its embedding + model (str): the model used for creating the embedding + embedding (List[float]): the values obtained from the message & model + + Raises: + OasstError: if misses some of the before params + + Returns: + MessageEmbedding: the instance in the database of the embedding saved for that message + """ + + if None in (message_id, model, embedding): + raise OasstError("Paramters missing to add embedding", OasstErrorCode.GENERIC_ERROR) + + message_embedding = MessageEmbedding(message_id=message_id, model=model, embedding=embedding) + self.db.add(message_embedding) + self.db.commit() + self.db.refresh(message_embedding) + return message_embedding + def insert_reaction(self, task_id: UUID, payload: db_payload.ReactionPayload) -> MessageReaction: if self.user_id is None: raise OasstError("User required", OasstErrorCode.USER_NOT_SPECIFIED) diff --git a/backend/oasst_backend/utils/hugging_face.py b/backend/oasst_backend/utils/hugging_face.py index 0df913f5..87c6288e 100644 --- a/backend/oasst_backend/utils/hugging_face.py +++ b/backend/oasst_backend/utils/hugging_face.py @@ -1,10 +1,21 @@ +from enum import Enum from typing import Any, Dict import aiohttp +from loguru import logger from oasst_backend.config import settings from oasst_shared.exceptions import OasstError, OasstErrorCode +class HfUrl(str, Enum): + HUGGINGFACE_TOXIC_ROBERTA = ("https://api-inference.huggingface.co/models/unitary/multilingual-toxic-xlm-roberta",) + HUGGINGFACE_FEATURE_EXTRACTION = "https://api-inference.huggingface.co/pipeline/feature-extraction" + + +class HfEmbeddingModel(str, Enum): + MINILM = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" + + class HuggingFaceAPI: """Class Object to make post calls to endpoints for inference in models hosted in HuggingFace""" @@ -41,6 +52,9 @@ class HuggingFaceAPI: async with session.post(self.api_url, headers=self.headers, json=payload) as response: # If we get a bad response if response.status != 200: + + logger.error(response) + logger.info(self.headers) raise OasstError( "Response Error Detoxify HuggingFace", error_code=OasstErrorCode.HUGGINGFACE_API_ERROR ) diff --git a/docker-compose.yaml b/docker-compose.yaml index 6bc42c51..224c9efd 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -11,6 +11,11 @@ services: image: sverrirab/sleep depends_on: [db, webdb, adminer, maildev, backend, redis] + # Used by CI automations. + ci: + image: sverrirab/sleep + depends_on: [db, webdb, maildev, backend, redis, web] + # This DB is for the FastAPI Backend. db: image: postgres @@ -95,6 +100,7 @@ services: - DEBUG_SKIP_API_KEY_CHECK=True - DEBUG_USE_SEED_DATA=True - MAX_WORKERS=1 + - DEBUG_SKIP_EMBEDDING_COMPUTATION=True depends_on: db: condition: service_healthy diff --git a/notebooks/README.md b/notebooks/README.md index edb5da33..2eea7a15 100644 --- a/notebooks/README.md +++ b/notebooks/README.md @@ -1,10 +1,14 @@ # Notebooks -This is a folders with some useful notebooks, all the notebooks have a markdown -file with the same name explaining what they do. +This is a folder with some useful notebooks, all the notebooks have a markdown +file with the same name (or a README.md if its a single notebook folder) +explaining what they do. ## Contributing -Contributing to both notebooks and making new notebooks is very welcome. If you -do so, make sure to make a markdown (.md) file to go with your notebook, makes +Contributing to notebooks and making new notebooks is very welcome. If you do +so, make sure to make a markdown (.md) file to go with your notebook, it makes it easier for people to know what your notebook is about. + +Check out the [example notebook](example/) for a reference example you can use +as a starting point. diff --git a/notebooks/example/README.md b/notebooks/example/README.md new file mode 100644 index 00000000..763b4812 --- /dev/null +++ b/notebooks/example/README.md @@ -0,0 +1,48 @@ +# Example Notebook + +[](https://colab.research.google.com/github/LAION-AI/Open-Assistant/blob/main/notebooks/example/example.ipynb) + +This folder contains an example reference notebook structure and approach for +this project. Please try and follow this structure as closely as possible. While +things will not exactly be the same for each notebook some principles we would +like to try ensure are: + +1. Each notebook or collection of related or dependant notebooks should live in + its own folder. +1. Each notebook should have a markdown file with the same name as the notebook + (or README.md if it's a single notebook folder) that explains what the + notebook does and how to use it. +1. Add an "Open in Colab" badge to the top of the notebook (see the markdown + cell near the top of `example.ipynb` as an example you can adapt). +1. Make it as easy as possible for someone to run the notebook in Google Colab + or some other environment based on standard practices like providing a + `requirements.txt` file or anything else needed to successfully run the + notebook. + +## Running in Google Colab + +At the top of the [example notebook](example.ipynb) there is a code cell that +will (once uncommented): + +1. clone the repository into your colab instance. +1. `cd` into the relevant notebook directory. +1. run `pip install -r requirements.txt` to install the required packages. + +At this point you can run the notebook as normal and the folder structure will +match that of the repository and the colab notebook will be running from the +same directory that the notebook lives in so relative links etc should work as +expected (for example `example.ipynb` will read some sample data from +`data/data.csv`). + +If you are adding a notebook please try and add a similar cell to the top of the +notebook so that it is easy for others to run the notebook in colab. If your +notebook does not have any dependencies beyond what already comes as standard in +Google Colab then you do not need such a cell, just an "Open in Colab" badge +will suffice. + +## example.ipynb + +This notebook contains an example "Open In Colab" badge and a code cell to +prepare the colab environment to run the notebook. It also contains a code cell +that will read in some sample data from the `data` folder in the repository and +display it as a pandas dataframe. diff --git a/notebooks/example/data/data.csv b/notebooks/example/data/data.csv new file mode 100644 index 00000000..126a03bb --- /dev/null +++ b/notebooks/example/data/data.csv @@ -0,0 +1,3 @@ +row,text,label +1,some example data,1 +2,some more data,0 diff --git a/notebooks/example/example.ipynb b/notebooks/example/example.ipynb new file mode 100644 index 00000000..5f938c13 --- /dev/null +++ b/notebooks/example/example.ipynb @@ -0,0 +1,161 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Example Notebook" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[](https://colab.research.google.com/github/LAION-AI/Open-Assistant/blob/example-notebook/notebooks/example/example.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# uncomment and run below lines to set up if running in colab\n", + "# !git clone https://github.com/LAION-AI/Open-Assistant.git\n", + "# %cd Open-Assistant/notebooks/example\n", + "# !pip install -r requirements.txt" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Try to add a markdown section to the notebook that explains what the notebook is about and what it does. This will help people understand what the notebook is for and how to use it." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# import required packages\n", + "import pandas as pd\n", + "from transformers import pipeline" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use Headings\n", + "\n", + "(it will help with link sharing to specific sections of the notebook)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make fancy markdown cells if you want." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
| \n", + " | row | \n", + "text | \n", + "label | \n", + "
|---|---|---|---|
| 0 | \n", + "1 | \n", + "some example data | \n", + "1 | \n", + "
| 1 | \n", + "2 | \n", + "some more data | \n", + "0 | \n", + "
{taskType.overview}
- {task.conversation ?