mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-07-13 00:50:06 +08:00
Merge branch 'LAION-AI:main' into main
This commit is contained in:
@@ -8,6 +8,9 @@ on:
|
||||
- ".github/workflows/deploy-docs-site.yaml"
|
||||
- "docs/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/deploy-docs-site.yaml"
|
||||
- "docs/**"
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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`.
|
||||
+54
-15
@@ -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
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
maxmemory 100mb
|
||||
maxmemory-policy allkeys-lru
|
||||
@@ -0,0 +1,2 @@
|
||||
[test]
|
||||
dev ansible_connection=local
|
||||
+27
@@ -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 ###
|
||||
+49
@@ -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 ###
|
||||
@@ -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 ###
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
+8
-4
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,3 @@
|
||||
row,text,label
|
||||
1,some example data,1
|
||||
2,some more data,0
|
||||
|
@@ -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": [
|
||||
"<div>\n",
|
||||
"<style scoped>\n",
|
||||
" .dataframe tbody tr th:only-of-type {\n",
|
||||
" vertical-align: middle;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .dataframe tbody tr th {\n",
|
||||
" vertical-align: top;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .dataframe thead th {\n",
|
||||
" text-align: right;\n",
|
||||
" }\n",
|
||||
"</style>\n",
|
||||
"<table border=\"1\" class=\"dataframe\">\n",
|
||||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>row</th>\n",
|
||||
" <th>text</th>\n",
|
||||
" <th>label</th>\n",
|
||||
" </tr>\n",
|
||||
" </thead>\n",
|
||||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>some example data</td>\n",
|
||||
" <td>1</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>2</td>\n",
|
||||
" <td>some more data</td>\n",
|
||||
" <td>0</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" row text label\n",
|
||||
"0 1 some example data 1\n",
|
||||
"1 2 some more data 0"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Do cool stuff here\n",
|
||||
"df = pd.read_csv(\"data/data.csv\")\n",
|
||||
"df.head()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.4 (tags/v3.7.4:e09359112e, Jul 8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)]"
|
||||
},
|
||||
"orig_nbformat": 4,
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "25d5c2324055587ceaeef27650c79ce8358ea61d7689f2e0b8ada5d53f85bce4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
transformers
|
||||
@@ -6,6 +6,7 @@ pushd "$parent_path/../../backend"
|
||||
|
||||
export DEBUG_SKIP_API_KEY_CHECK=True
|
||||
export DEBUG_USE_SEED_DATA=True
|
||||
export DEBUG_SKIP_EMBEDDING_COMPUTATION=True
|
||||
|
||||
uvicorn main:app --reload --port 8080 --host 0.0.0.0
|
||||
|
||||
|
||||
@@ -39,5 +39,7 @@ next-env.d.ts
|
||||
*.swp
|
||||
|
||||
# cypress
|
||||
/cypress/screenshots
|
||||
/cypress/videos
|
||||
/cypress-visual-screenshots/diff
|
||||
/cypress-visual-screenshots/comparison
|
||||
|
||||
@@ -153,6 +153,34 @@ When writing code for the website, we have a few best practices:
|
||||
1. Define functional React components (with types for all properties when
|
||||
feasible).
|
||||
|
||||
### Developing New Features
|
||||
|
||||
When working on new features or making significant changes that can't be done
|
||||
within a single Pull Request, we ask that you make use of Feature Flags.
|
||||
|
||||
We've set up
|
||||
[`react-feature-flags`](https://www.npmjs.com/package/react-feature-flags) to
|
||||
make this easier. To get started:
|
||||
|
||||
1. Add a new flag entry to `website/src/flags.ts`. We have an example flag you
|
||||
can copy as an example. Be sure to `isActive` to true when testing your
|
||||
features but false when submitting your PR.
|
||||
1. Use your flag wherever you add a new UI element. This can be done with:
|
||||
|
||||
```js
|
||||
import { Flags } from "react-feature-flags";
|
||||
...
|
||||
<Flags authorizedFlags={["yourFlagName"]}>
|
||||
<YourNewComponent />
|
||||
</Flags>
|
||||
```
|
||||
|
||||
You can see an example of how this works by checking `website/src/components/Header/Headers.tsx` where we use `flagTest`.
|
||||
|
||||
1. Once you've finished building out the feature and it is ready for everyone
|
||||
to use, it's safe to remove the `Flag` wrappers around your component and
|
||||
the entry in `flags.ts`.
|
||||
|
||||
### URL Paths
|
||||
|
||||
To use stable and consistent URL paths, we recommend the following strategy for
|
||||
|
||||
Generated
+17
@@ -39,6 +39,7 @@
|
||||
"postcss-focus-visible": "^7.1.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-feature-flags": "^1.0.0",
|
||||
"react-icons": "^4.7.1",
|
||||
"swr": "^2.0.0",
|
||||
"tailwindcss": "^3.2.4",
|
||||
@@ -26424,6 +26425,16 @@
|
||||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
|
||||
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
|
||||
},
|
||||
"node_modules/react-feature-flags": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-feature-flags/-/react-feature-flags-1.0.0.tgz",
|
||||
"integrity": "sha512-KBFUkXjF7ifGWEQr2Ida4LdAtKGDOwFdTRlXipWxGP9a43vUBxP6IscpYQofGjlzlBcgmFKuzubcVheB6NliEg==",
|
||||
"peerDependencies": {
|
||||
"prop-types": "^15.5.4",
|
||||
"react": ">= 16.3.0",
|
||||
"react-dom": ">= 16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-focus-lock": {
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.2.tgz",
|
||||
@@ -50838,6 +50849,12 @@
|
||||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
|
||||
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
|
||||
},
|
||||
"react-feature-flags": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-feature-flags/-/react-feature-flags-1.0.0.tgz",
|
||||
"integrity": "sha512-KBFUkXjF7ifGWEQr2Ida4LdAtKGDOwFdTRlXipWxGP9a43vUBxP6IscpYQofGjlzlBcgmFKuzubcVheB6NliEg==",
|
||||
"requires": {}
|
||||
},
|
||||
"react-focus-lock": {
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.2.tgz",
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
"postcss-focus-visible": "^7.1.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-feature-flags": "^1.0.0",
|
||||
"react-icons": "^4.7.1",
|
||||
"swr": "^2.0.0",
|
||||
"tailwindcss": "^3.2.4",
|
||||
|
||||
@@ -27,8 +27,22 @@ import poster from "src/lib/poster";
|
||||
import { colors } from "styles/Theme/colors";
|
||||
import useSWRMutation from "swr/mutation";
|
||||
|
||||
interface textFlagLabels {
|
||||
attributeName: string;
|
||||
labelText: string;
|
||||
additionalExplanation?: string;
|
||||
}
|
||||
|
||||
export const FlaggableElement = (props) => {
|
||||
const [isEditing, setIsEditing] = useBoolean();
|
||||
const flaggable_labels = props.flaggable_labels;
|
||||
const TEXT_LABEL_FLAGS = flaggable_labels.valid_labels.map((valid_label) => {
|
||||
return {
|
||||
attributeName: valid_label.name,
|
||||
labelText: valid_label.display_text,
|
||||
additionalExplanation: valid_label.help_text,
|
||||
};
|
||||
});
|
||||
const { trigger } = useSWRMutation("/api/set_label", poster, {
|
||||
onSuccess: () => {
|
||||
setIsEditing.off;
|
||||
@@ -181,48 +195,3 @@ export function FlagCheckbox(props: {
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
interface textFlagLabels {
|
||||
attributeName: string;
|
||||
labelText: string;
|
||||
additionalExplanation?: string;
|
||||
}
|
||||
const TEXT_LABEL_FLAGS: textFlagLabels[] = [
|
||||
// For the time being this list is configured on the FE.
|
||||
// In the future it may be provided by the API.
|
||||
// {
|
||||
// attributeName: "fails_task",
|
||||
// labelText: "Fails to follow the correct instruction / task",
|
||||
// additionalExplanation: "__TODO__",
|
||||
// },
|
||||
// {
|
||||
// attributeName: "not_customer_assistant_appropriate",
|
||||
// labelText: "Inappropriate for customer assistant",
|
||||
// additionalExplanation: "__TODO__",
|
||||
// },
|
||||
{
|
||||
attributeName: "sexual_content",
|
||||
labelText: "Contains sexual content",
|
||||
},
|
||||
{
|
||||
attributeName: "violence",
|
||||
labelText: "Contains violent content",
|
||||
},
|
||||
// {
|
||||
// attributeName: "encourages_violence",
|
||||
// labelText: "Encourages or fails to discourage violence/abuse/terrorism/self-harm",
|
||||
// },
|
||||
// {
|
||||
// attributeName: "denigrates_a_protected_class",
|
||||
// labelText: "Denigrates a protected class",
|
||||
// },
|
||||
// {
|
||||
// attributeName: "gives_harmful_advice",
|
||||
// labelText: "Fails to follow the correct instruction / task",
|
||||
// additionalExplanation:
|
||||
// "The advice given in the output is harmful or counter-productive. This may be in addition to, but is distinct from the question about encouraging violence/abuse/terrorism/self-harm.",
|
||||
// },
|
||||
// {
|
||||
// attributeName: "expresses_moral_judgement",
|
||||
// labelText: "Expresses moral judgement",
|
||||
// },
|
||||
];
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Box, Button, Text, useColorMode } from "@chakra-ui/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { Flags } from "react-feature-flags";
|
||||
import { FaUser } from "react-icons/fa";
|
||||
|
||||
import { UserMenu } from "./UserMenu";
|
||||
@@ -42,6 +43,9 @@ export function Header(props) {
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<Flags authorizedFlags={["flagTest"]}>
|
||||
<div>FlagTest</div>
|
||||
</Flags>
|
||||
<AccountButton />
|
||||
<UserMenu />
|
||||
</div>
|
||||
|
||||
@@ -10,11 +10,31 @@ export interface Message {
|
||||
message_id: string;
|
||||
}
|
||||
|
||||
export const Messages = ({ messages, post_id }: { messages: Message[]; post_id: string }) => {
|
||||
export interface ValidLabel {
|
||||
name: string;
|
||||
display_text: string;
|
||||
help_text: string;
|
||||
}
|
||||
|
||||
export const Messages = ({
|
||||
messages,
|
||||
post_id,
|
||||
valid_labels,
|
||||
}: {
|
||||
messages: Message[];
|
||||
post_id: string;
|
||||
valid_labels: ValidLabel[];
|
||||
}) => {
|
||||
const items = messages.map((messageProps: Message, i: number) => {
|
||||
const { message_id, text } = messageProps;
|
||||
return (
|
||||
<FlaggableElement text={text} post_id={post_id} message_id={message_id} key={i + text}>
|
||||
<FlaggableElement
|
||||
text={text}
|
||||
post_id={post_id}
|
||||
message_id={message_id}
|
||||
key={i + text}
|
||||
flaggable_labels={valid_labels}
|
||||
>
|
||||
<MessageView {...messageProps} />
|
||||
</FlaggableElement>
|
||||
);
|
||||
|
||||
@@ -18,7 +18,7 @@ export interface CreateTaskProps {
|
||||
}
|
||||
export const CreateTask = ({ tasks, taskType, trigger, onSkipTask, onNextTask, mainBgClasses }: CreateTaskProps) => {
|
||||
const task = tasks[0].task;
|
||||
|
||||
const valid_labels = tasks[0].valid_labels;
|
||||
const [inputText, setInputText] = useState("");
|
||||
|
||||
const submitResponse = (task: { id: string }) => {
|
||||
@@ -42,7 +42,9 @@ export const CreateTask = ({ tasks, taskType, trigger, onSkipTask, onNextTask, m
|
||||
<>
|
||||
<h5 className="text-lg font-semibold">{taskType.label}</h5>
|
||||
<p className="text-lg py-1">{taskType.overview}</p>
|
||||
{task.conversation ? <Messages messages={task.conversation.messages} post_id={task.id} /> : null}
|
||||
{task.conversation ? (
|
||||
<Messages messages={task.conversation.messages} post_id={task.id} valid_labels={valid_labels} />
|
||||
) : null}
|
||||
</>
|
||||
<>
|
||||
<h5 className="text-lg font-semibold">{taskType.instruction}</h5>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
const flags = [{ name: "flagTest", isActive: false }];
|
||||
|
||||
export default flags;
|
||||
@@ -48,6 +48,33 @@ export class OasstApiClient {
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
private async get(path: string): Promise<any> {
|
||||
const resp = await fetch(`${this.oasstApiUrl}${path}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"X-API-Key": this.oasstApiKey,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (resp.status == 204) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (resp.status >= 300) {
|
||||
const errorText = await resp.text();
|
||||
let error: any;
|
||||
try {
|
||||
error = JSON.parse(errorText);
|
||||
} catch (e) {
|
||||
throw new OasstError(errorText, 0, resp.status);
|
||||
}
|
||||
throw new OasstError(error.message ?? error, error.error_code, resp.status);
|
||||
}
|
||||
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
// TODO return a strongly typed Task?
|
||||
// This method is used to store a task in RegisteredTask.task.
|
||||
// This is a raw Json type, so we can't use it to strongly type the task.
|
||||
@@ -96,6 +123,12 @@ export class OasstApiClient {
|
||||
...content,
|
||||
});
|
||||
}
|
||||
|
||||
//Fetch valid labels. This is called every task. though the call may be redundant
|
||||
//keeping this for future where the valid labels may change per task
|
||||
async fetch_valid_text(): Promise<void> {
|
||||
return this.get(`/api/v1/text_labels/valid_labels`);
|
||||
}
|
||||
}
|
||||
|
||||
export const oasstApiClient =
|
||||
|
||||
@@ -3,7 +3,9 @@ import "focus-visible";
|
||||
|
||||
import type { AppProps } from "next/app";
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
import { FlagsProvider } from "react-feature-flags";
|
||||
import { getDefaultLayout, NextPageWithLayout } from "src/components/Layout";
|
||||
import flags from "src/flags";
|
||||
|
||||
import { Chakra, getServerSideProps } from "../styles/Chakra";
|
||||
|
||||
@@ -16,9 +18,11 @@ function MyApp({ Component, pageProps: { session, cookies, ...pageProps } }: App
|
||||
const page = getLayout(<Component {...pageProps} />);
|
||||
|
||||
return (
|
||||
<Chakra cookies={cookies}>
|
||||
<SessionProvider session={session}>{page}</SessionProvider>
|
||||
</Chakra>
|
||||
<FlagsProvider value={flags}>
|
||||
<Chakra cookies={cookies}>
|
||||
<SessionProvider session={session}>{page}</SessionProvider>
|
||||
</Chakra>
|
||||
</FlagsProvider>
|
||||
);
|
||||
}
|
||||
export { getServerSideProps };
|
||||
|
||||
@@ -23,6 +23,7 @@ const handler = async (req, res) => {
|
||||
|
||||
// Fetch the new task.
|
||||
const task = await oasstApiClient.fetchTask(task_type, token);
|
||||
const valid_labels = await oasstApiClient.fetch_valid_text();
|
||||
|
||||
// Store the task and link it to the user..
|
||||
const registeredTask = await prisma.registeredTask.create({
|
||||
@@ -36,6 +37,11 @@ const handler = async (req, res) => {
|
||||
},
|
||||
});
|
||||
|
||||
// Add the valid labels that can be used to flag messages in this Task
|
||||
registeredTask["valid_labels"] = valid_labels;
|
||||
// Update the backend with our Task ID
|
||||
await oasstApiClient.ackTask(task.id, registeredTask.id);
|
||||
|
||||
// Send the results to the client.
|
||||
res.status(200).json(registeredTask);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user