The contents of exponent.txt are included whenever a message is handled by Exponent, helping it understand your project’s structure, conventions, and best practices.
If you are familiar with .cursorrules or .github/copilot-instructions.md files, exponent.txt is similar in spirit for Exponent.
Here’s a generic example of an exponent.txt file for a project. Notice that it does not adhere completely to the instructions above, but is catered to the specific project, developers, and types of tasks that Exponent is being used for.
This is a FastAPI application using sqlalchemy for the backend of a social application. It makes liberal use of async features of all libraries.
General instructions
Make sure to model any new models, schemas, tests, or endpoints/routes very closely against the existing examples you have. Feel free to ask for more examples if you don’t feel like you have a clear one to use.
Always try to write tests if it makes sense.
If you feel there’s something missing in the instructions that will hinder your ability to do a task, DO NOT BEGIN THE TASK. Instead, tell me about the issues you see by asking questions and providing suggestions.
Dependencies and versions
Make sure the code you write adheres to the best practices of the major versions (or later) of the dependencies we work with. Here are some of the main ones to pay attention to:
FastAPI 0.110.0
SQLAlchemy 2.0.28
Pydantic 2.6.4
Celery 5.3.6
Firebase Admin 6.5.0
Alembic 1.13.1
Tests
Tests live in tests/ and are generally one file per-route. So, tests for GET /friends go in tests/test_get_friends.py
You can always reference the following files for examples of clean, well-written tests: tests/test_add_user_device.py and tests/test_update_user_device.py
Here are some commonly used fixtures you have available in tests:
@pytest_asyncio.fixture(name=“session”, scope=“function”) -> returns a database session
@pytest_asyncio.fixture(name=“client”, scope=“function”) -> returns a test AsyncClient to use
@pytest_asyncio.fixture(name=“default_user”, scope=“function”) -> returns a default User object
@pytest.fixture(name=“default_user_headers”, scope=“function”) -> returns a default set of headers to authenticate requests made by the test client
For patching external services so that there aren’t any real network requests made in tests, here’s an example test using a mock object correctly:
Endpoints are resource-based and live in api/endpoints; for example, new endpoints for “friends” goes in api/endpoints/friends.py
Any new router files should be added appropriately to api/api_router.py, following the examples provided therein.
You can always reference a cleanly written endpoint at api/endpoints/devices.py
All API messages are stored as constants in api/api_messages.py. If you are referencing or adding any API messages make sure you are not using strings willy-nilly.
Anything that interacts with an external service like S3 can be found in external_services.py.
New fields
If you are instructed to add a field, you must put the new field in the appropriate places in all of the following files (example given for adding an is_active field to the User object):
models.py: Add it to the model with the correct sqlalchemy column mapping (e.g. in the User model, add: is_active: Mapped[bool] = mapped_column( Boolean, default=True, nullable=False,))
schemas.py: Add it to any appropriate schemas (e.g. UserResponse)
If a field in a schema contains an enum, make sure the model_config for that schema includes use_enum_values=True
Any relevant API endpoints you have access to
Afterwards, provide a reasonable name for the corresponding Alembic migration.