Skip to content

Commit

Permalink
QA on Framework pages based on Ramiro feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
anant-writer committed Jun 21, 2024
1 parent 7b4c660 commit fd61085
Show file tree
Hide file tree
Showing 10 changed files with 38 additions and 64 deletions.
12 changes: 3 additions & 9 deletions docs/framework/ai-module.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ You can manage your environment variables using methods that best suit your setu

Furthermore, when deploying an application with `writer deploy`, the `WRITER_API_KEY` environment variable is automatically configured with the API key specified during the deployment process.

## Conversation class
## Chat completion with the Conversation class
The `Conversation` class manages LLM communications within a chat framework, storing the conversation history and handling the interactions.

```python
Expand Down Expand Up @@ -74,13 +74,7 @@ conversation += {"role": "user", "content": "Can you break down the assets secti
conversation.add(role="user", content="How should I interpret the equity section?")
```

Addition to `Conversation` only works against `dict` objects that contain `"role"` and `"content"` items. Providing a `"chunk": True` flag into the object will merge it against the last message - appending `"content"` and replacing other values.

### Completing and streaming Conversations
<Tip>
When utilizing the `stream_complete` feature, the initial chunk of data returned by the stream is not specifically marked as a "chunk." This is intentional, allowing for the seamless integration of this first piece into the conversation history when appended using the + operator.
</Tip>

The `complete` and `stream_complete` methods facilitate interaction with the LLM based on the accumulated messages and configuration. These methods execute calls to generate responses and return them in the form of a message object, but do not alter the conversation's `messages` list, allowing you to validate or modify the output before deciding to add it to the history.

<CodeGroup>
Expand All @@ -105,8 +99,8 @@ Instance-wide configuration parameters can be complemented or overriden on indiv
response = conversation.complete(config={'max_tokens': 200, 'temperature': 0.5})
```

## Text completions without a conversation state
These `complete` and `stream_complete` methods are designed for one-off text completions without the need to manage a conversation state. They return the model's response as a string. Each function accepts a `config` dictionary allowing call-specific configurations.
## Text generation without a conversation state
These `complete` and `stream_complete` methods are designed for one-off text generation without the need to manage a conversation state. They return the model's response as a string. Each function accepts a `config` dictionary allowing call-specific configurations.

<CodeGroup>
```python complete
Expand Down
46 changes: 15 additions & 31 deletions docs/framework/authentication.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
title: "Authentication"
---

The Writer Framework authentication module allows you to restrict access to your application.

Framework will be able to authenticate a user through an identity provider such as Google, Microsoft, Facebook, Github, Auth0, etc.
The Writer Framework authentication module allows you to restrict access to your application. Framework will be able to authenticate a user through an identity provider such as Google, Microsoft, Facebook, Github, Auth0, etc.

<Warning>
Authentication is done before accessing the application. It is not possible to
Expand All @@ -13,19 +11,13 @@ Framework will be able to authenticate a user through an identity provider such

## Use Basic Auth

Basic Auth is a simple authentication method that uses a username and password. Authentication configuration is done in [the `server_setup.py` module](custom-server.md).

::: warning Password authentication is not safe for critical application
Basic Auth authentication is not secure for critical applications.
Basic Auth is a simple authentication method that uses a username and password. Authentication configuration is done in the [server_setup.py module](/framework/custom-server).

A user can intercept the plaintext password if https encryption fails.
It may also try to force password using brute force attacks.

For added security, it's recommended to use identity provider (Google, Microsoft, Facebook, Github, Auth0, etc.).
:::
<Warning>
Password authentication and Basic Auth are not sufficiently secure for critical applications. If HTTPS encryption fails, a user could potentially intercept passwords in plaintext. Additionally, these methods are vulnerable to brute force attacks that attempt to crack passwords. To enhance security, it is advisable to implement authentication through trusted identity providers such as Google, Microsoft, Facebook, GitHub, or Auth0.
</Warning>

*server_setup.py*
```python
```python server_setup.py
import os
import writer.serve
import writer.auth
Expand All @@ -41,12 +33,12 @@ writer.serve.register_auth(auth)
### Brute force protection

A simple brute force protection is implemented by default. If a user fails to log in, the IP of this user is blocked.
Writer framework will ban the IP from either the X-Forwarded-For header or the X-Real-IP header or the client IP address.
Writer framework will ban the IP from either the `X-Forwarded-For` header or the `X-Real-IP` header or the client IP address.

When a user fails to log in, they wait 1 second before they can try again. This time can be modified by
modifying the value of delay_after_failure.
modifying the value of `delay_after_failure`.

<img src="./images/auth_too_many_request.png" style="width: 100%; margin: auto">
<img src="/framework/images/429.png" />

## Use OIDC provider

Expand All @@ -55,9 +47,7 @@ Here is an example configuration for Google.

![Authentication OIDC Principle](/framework/images/auth.png)

**server_setup.py**

```python
```python server_setup.py
import os
import writer.serve
import writer.auth
Expand Down Expand Up @@ -88,9 +78,7 @@ The Writer Framework provides pre-configured OIDC providers. You can use them di

You have to register your application into [Google Cloud Console](https://console.cloud.google.com/).

_server_setup.py_

```python
```python server_setup.py
import os
import writer.serve
import writer.auth
Expand All @@ -108,9 +96,7 @@ writer.serve.register_auth(oidc)

You have to register your application into [Github](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app#registering-a-github-app)

_server_setup.py_

```python
```python server_setup.py
import os
import writer.serve
import writer.auth
Expand All @@ -128,9 +114,8 @@ writer.serve.register_auth(oidc)

You have to register your application into [Auth0](https://auth0.com/).

_server_setup.py_

```python
```python server_setup.py
import os
import writer.serve
import writer.auth
Expand All @@ -147,13 +132,14 @@ writer.serve.register_auth(oidc)

### Authentication workflow

<img src="./framework/images/authentication_oidc.png" />
<img src="/framework/images/authentication_oidc.png" />

## User information in event handler

When the `user_info` route is configured, user information will be accessible
in the event handler through the `session` argument.


```python
def on_page_load(state, session):
email = session['userinfo'].get('email', None)
Expand Down Expand Up @@ -189,8 +175,6 @@ The default authentication error page look like this:

<img src="/framework/images/auth_unauthorized_default.png" />

_streamsync.auth.Unauthorized_

| Parameter | Description |
| ----------- | ---------------------- |
| status_code | HTTP status code |
Expand Down
8 changes: 4 additions & 4 deletions docs/framework/backend-driven-ui.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ with ui.find(container):

### Component methods

UI manager contains methods linked to each front-end component. For example, in previous code snippets we provide a `ui.Text` method, which is used for creating [Text components](https://www.streamsync.cloud/component-list.html#text).
UI manager contains methods linked to each front-end component. For example, in previous code snippets we provide a `ui.Text` method, which is used for creating [Text components](https://dev.writer.com/components/text).

This method expects `content: dict` as first argument, which enables you to set the field properties of the component, through corresponding keys:
```python
Expand Down Expand Up @@ -147,7 +147,7 @@ In addition to `content`, a set of fields which is specific to the component typ
</Note>
- **`position: int`**: Determines the display order of the component in relation to its siblings.
Position `0` means that the component is the first child of its parent.
Position `-2` is used for components – such as [sidebars](https://www.streamsync.cloud/component-list.html#sidebar) – that have a specific reserved position not related to their siblings.
Position `-2` is used for components – such as [sidebars](https://dev.writer.com/components/sidebar) – that have a specific reserved position not related to their siblings.
```python
ui.Text(
{"text": "Hello Parent, I'm your first child!"},
Expand All @@ -170,7 +170,7 @@ In addition to `content`, a set of fields which is specific to the component typ

ui.Text({"text": "My visibility depends on the @{my_var}!"}, visible="my_var")
```
- **`handlers: dict[str, callable]`**: Attaches [event handlers](https://www.streamsync.cloud/event-handlers.html) to the component. Each dictionary key represents an event, and its value is the corresponding handler.:
- **`handlers: dict[str, callable]`**: Attaches [event handlers](https://dev.writer.com/framework/event-handlers) to the component. Each dictionary key represents an event, and its value is the corresponding handler.:
```python
def increment(state):
state["counter"] += 1
Expand All @@ -190,7 +190,7 @@ In addition to `content`, a set of fields which is specific to the component typ
# Both approaches yield the same outcome.
```
*A component can be linked to multiple event handlers.*
- **`binding: dict[str, str]`**: Links the component to a state variable via [binding](https://www.streamsync.cloud/builder-basics.html#binding). The dictionary key is the bindable event, and the value is the state variable's name:
- **`binding: dict[str, str]`**: Links the component to a state variable via [binding](https://dev.writer.com/framework/builder-basics#binding). The dictionary key is the bindable event, and the value is the state variable's name:
```python
initial_state = wf.init_state({
"header_text": "Default Text"
Expand Down
4 changes: 2 additions & 2 deletions docs/framework/custom-components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default {
};
</script>
<script setup lang="ts">
import { FieldType } from "../streamsyncTypes";
import { FieldType } from "../writerTypes";
import injectionKeys from "../injectionKeys";
import { inject } from "vue";

Expand All @@ -100,7 +100,7 @@ The code above will make Bubble Message available in the Builder.

<Steps>
<Step title="Clone the Framework Repository">
To get started, clone the [Framework repository](https://github.com/streamsync-cloud/streamsync) from GitHub.
To get started, clone the [Framework repository](https://github.com/writer/writer-framework) from GitHub.
</Step>
<Step title="Set Up the Development Environment">
To develop custom templates in a developer-friendly way, ensure you have a front-end development server with instant reload capabilities. The front-end code for Framework is located in the `ui` folder. With Node and npm installed on your system, run `npm install` to install dependencies. Then, start the server with support for custom component templates using `npm run custom.dev`.
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/custom-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def hello():
return "1"
```
<Warning>
Use `server_setup.py` in `edit` mode. If you want to use in `edit` mode, you can launch `writer edit --enable-server-setup <app>`.
`server_setup.py` is disabled by default on edit mode. If you want to use in `edit` mode, you can launch `writer edit --enable-server-setup <app>`.
</Warning>

## Implement custom server
Expand Down
10 changes: 5 additions & 5 deletions docs/framework/deploy-with-docker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ If you're a Docker expert, feel free to work on your own `Dockerfile`. Framework
To build the image, use `docker build . -t ` followed by an image tag, which you're free to choose and will locally identify your image.

```sh
docker build . -t my_streamsync_app
docker build . -t my_framework_app
```

<Warning>
Expand All @@ -67,16 +67,16 @@ You can push your image using the following commands.
docker login

# Push the image
# Replace "my_streamsync_app" for the tag you've previously chosen
# Replace "my_framework_app" for the tag you've previously chosen
# Replace "my_user" for your user on Docker Hub
docker tag my_streamsync_app:latest my_user/my_streamsync_app:latest
docker push my_user/my_streamsync_app:latest
docker tag my_framework_app:latest my_user/my_framework_app:latest
docker push my_user/my_framework_app:latest
```

If your image is public, anyone can now run the following command and start the app. It's important to bind the port to a port in the host machine. The command below binds port 8080 in the Docker image to port 8080 in the host.

```sh
docker run -p 8080:8080 my_user/my_streamsync_app
docker run -p 8080:8080 my_user/my_framework_app
```

Go on your browser to [http://localhost:8080](http://localhost:8080) to check everything is working as expected.
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/event-handlers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Note that for this "chain" to work, you need to import the final module in the s

## Mutating state

In most cases, event handlers will modify the application state. State can be accessed by including the `state` argument in the handler, which will provide you with a `StreamsyncState` object for the session that invoked the handler.
In most cases, event handlers will modify the application state. State can be accessed by including the `state` argument in the handler, which will provide you with a `WriterState` object for the session that invoked the handler.

Elements of state can be reached using the square brackets syntax `state["my_element"]`. Accessing keys that don't exist will return `None`.

Expand Down
4 changes: 0 additions & 4 deletions docs/framework/frontend-scripts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,6 @@ initial_state.import_script("lodash", "https://cdnjs.cloudflare.com/ajax/libs/lo

## Frontend core

<Warning>
Effectively using Framework's core can be challenging and will likely entail reading its [source code](https://github.com/streamsync-cloud/streamsync/blob/master/ui/src/core/index.ts). Furthermore, it's considered an internal capability rather than a public API, so it may unexpectedly change between releases.
</Warning>

You can access Framework's front-end core via `globalThis.core`, unlocking all sorts of functionality. Notably, you can use `getUserState()` to get values from state.

```js
Expand Down
8 changes: 4 additions & 4 deletions docs/framework/state-schema.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ scenarios, while also allowing your IDE and toolchains to provide autocomplete a
```python
import writer as wf

class AppSchema(wf.StreamsyncState):
class AppSchema(wf.WriterState):
counter: int

initial_state = wf.init_state({
Expand Down Expand Up @@ -50,7 +50,7 @@ Schema composition allows you to model a complex Application state.
class MyappSchema(wf.State):
title: str

class AppSchema(wf.StreamsyncState):
class AppSchema(wf.WriterState):
my_app: MyappSchema
counter: int

Expand All @@ -70,7 +70,7 @@ A schema allows you to specify that an attribute which contains a dictionary
must be treated as a dictionary, rather than as a group of state.

```python
class AppSchema(wf.StreamsyncState):
class AppSchema(wf.WriterState):
vegas_graph: dict

# Without schema, this handler is execute only once
Expand Down Expand Up @@ -112,7 +112,7 @@ Here is the code, can you spot the error ?
```python
import writer as wf

class AppSchema(wf.StreamsyncState):
class AppSchema(wf.WriterState):
counter: int

def increment(state: AppSchema):
Expand Down
6 changes: 3 additions & 3 deletions docs/framework/testing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ To get started, import your app's entry point, `main`. This will initialise stat

### Creating states

For testing purposes, you can create your own state using the `StreamsyncState` class in `writer.core`. Pass a dictionary when constructing it.
For testing purposes, you can create your own state using the `WriterState` class in `writer.core`. Pass a dictionary when constructing it.

```py
from writer.core import StreamsyncState
from writer.core import WriterState

artificial_state = StreamsyncState({
artificial_state = WriterState({
"a": 3,
"b": 6
})
Expand Down

0 comments on commit fd61085

Please sign in to comment.