Giter Club home page Giter Club logo

diagramastext's Introduction

Diagram As Text

The tool to generate diagram based on textual description. Although it takes quite some effort to make a diagram, LLM is here for the rescue! πŸ€–πŸ¦Ύ

demo_beta.mp4

πŸš€πŸš€πŸš€ Mission: to enable anyone to explain complex system, or process in a simple way.

πŸš€πŸš€ Objective: to streamline knowledge sharing through diagrams.

πŸš€ MVP: plain english to C4 container diagrams.

πŸ”” Wanted: founding contributors πŸ””

The project is purely community driven - we need your support:

  • Please give the project a star.
  • Join us as contributor: we need software engineers, data scientists, analysts, designers.

If you are excited about the project, feel comfortable with our ways of work, and want to contribute, please get in touch for further details.

Thank you! πŸ™

Contacts

Outlook

Ways of work

Manifesto

  • We are driven by the mission
  • We respect one another and the community
  • We deliver in lean iterations
  • We work async with pairing programming sessions
  • We share the work openly, see the license details

Process

  • We follow TDD
  • We follow RDD
  • We maintain flat modules structure whether possible
  • We embrace clean architecture inspired by Alistair Cockburn's port-adapter approach and Martin Fowler's inversion of control, see a great write-up here on the subjects matter
  • We aim for simplicity with the least external dependencies
  • We follow conventional comments guideline for code reviews
  • We follow conventional commits guideline:
    • feat: for features
    • fix: for defect fix
    • chore: for infra, ci, or docs adjustments; or refactoring
  • We follow the monorepo approach
  • We follow trunk-based development model
  • We follow the release guideline and semantic versioning

Bets/Panning/Commitment

Tech details

Tech stack
  • Languages:
    • Go 1.19
    • TypeScript, ES2020:
    • Python 3.9
  • Markup:
    • Markdown
    • HTML5
    • CSS3
  • CI:
    • GitHub Actions
  • Infra:
    • GCP
      • IAM
      • SecretsManager
      • CloudRun
      • IdentityPlatform
    • AWS (infra deprecated as of 2023-03)
    • GitHub Pages
    • Neon
    • Cloudflare
    • namecheap
    • godaddy
  • Tools:
    • gnuMake
    • Docker
    • terraform
  • Logic:
    • PlantUML
    • OpenAI

Architecture

C4 Context Level

architecture-context

Local setup

Requirements

  • git
  • gnuMake
  • docker ~> 20.10

Steps

Follow the steps to provision local development environment.

  1. Clone the repo
git clone [email protected]:kislerdm/diagramastext.git
  1. Export OpenAI access token as the environment variable OPENAI_API_KEY:
export OPENAI_API_KEY=##TOKEN##
  1. Run to provision the local environment:
make localenv

Note:

  1. Run to clean the local environment:
make localenv-teardown

License

Codebase

The codebase is distributed under the Apache 2.0 licence.

Images and diagrams

Creative Commons Licence
This work by diagramastext.dev is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Based on a work at https://github.com/kislerdm/diagramastext. Permissions beyond the scope of this license may be available at diagramastext.dev.

diagramastext's People

Contributors

dependabot[bot] avatar kislerdm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

coledrain

diagramastext's Issues

As a user, I want generated diagrams to correspond to my description with high accuracy

Problem

v0.0.1 of the product relies on a general codex model which performs poorly on non-trivial prompts. We identified the strategy to improve the app's output quality. It requires use of the prompts submitted by our users.

Proposed Solution

Store the input prompt and the model generated graph to a database.

Acceptance Criteria

  • Every prompt and model-generated graph is stored to a Neon postgres db

Define infra and deploy the product

Infra:

  • backend:
    • server deployment
    • apigateway
      • adjust CORS configuration: Access-Control-Allow-Origin: https://diagramascode.dev
    • custom url pointing to api.diagramastext.dev
    • cloudflare setup
  • webui:
    • github pages: diagramascode.dev with the logic

Add OpenAI inference logic

Problem

User input text has to be converted to the instructions for PlantUML to generate diagram.

Proposed Solution

Develop ClientOpenAI to communicate with OpenAI over HTTP using the Rest API:

  • The client will configured with the following attributes:
    • token
    • organization
    • modelID
  • The http client shall implement a middleware to authenticate with OpenAI, see the docu
  • The client's interface will have the method InferModel(prompt string) to call the model, see the contract.
  • The InferModel output will contain the DiagramGraph object:
type DiagramGraph struct {
	Title  string
	Footer string
	Nodes  []*Node
	Links  []*Link
}

type Node struct {
	ID         string `json:"id"`
	Label      string `json:"label"`
	Group      string `json:"group"`
	Technology string `json:"technology"`
	External   bool   `json:"external"`
	IsQueue    bool   `json:"is_queue"`
	IsDatabase bool   `json:"is_database"`
}

type Link struct {
	From       string `json:"from"`
	To         string `json:"to"`
	Direction  string `json:"direction"`
	Label      string `json:"label"`
	Technology string `json:"technology"`
}

DoD

  • The logic is implemented in the openai.go
  • Test coverage is at least 80%

As a user, I want to have the legend by default as in the app demo.

Problem

Legend is not present in the diagram image despite being shown in the example and demo video.

Proposed Solution

  • Add legend by default
  • Remove legend upon an explicit prompt only

Acceptance Criteria

Legend is present by default unless otherwise is requested in the input prompt.

As the service provider, I want to keep costs under control

Problem

The free codex models will be discontinued by openAI on 2023-03-23. It means that every user's inquiry will generate ops costs.

Proposed Solution

  • Add tracking of the OpenAI usage as the following columns of the table openai_response in db:
    • prompt_tokens, smallint
    • completion_tokens, smallint
    • model: text
    • is_success: bool

DoD

  • The table openai_response adjusted
  • The core logic stores the model response regardless of whether graph generation was successful or not to keep track of unsuccessful user's requests (no diagram generated) which we pay for to OpenAI
    Example:
    • Prompt: "asdasd"
    • Response:
{"id":"chatcmpl-71vlYWvLGIlVNrhVWe63qFmxofsPT","object":"chat.completion","created":1680694736,"model":"xxxxx","usage":{"prompt_tokens":775,"completion_tokens":28,"total_tokens":803},"choices":[{"message":{"role":"assistant","content":"I'm sorry, I don't understand what you mean by \"asdasd\". Can you please provide more context or a specific request?"},"finish_reason":"stop","index":0}]}

As a user, I want to have extended usage plan

Problem

All users have a single plan for now. Many users would want to extend the quota, or have case-tailored usage conditions.

Proposed Solution

Implement user's authentication and authorisation using JWT.

References:

End-to-end flow: webclient

sequenceDiagram
     User->>Webclient: Initiates diagram's generation
     Webclient-->>Browser Cookies: Look up JWT tokens

alt "Login/Sign-in flow", the refresh token not found
     Webclient->>User: Login page
alt User opts-in for login
     User->>Webclient: Enter email address 
     Webclient-->Webclient: Login page's state transition: "input secret"
     Webclient->>AuthService: Call "/auth/email"

else User wants to continue without authN
     Webclient-->Webclient: Generate user's "fingerprint"
     Webclient->>AuthService: Call "/auth/anonym"
end

     AuthService-->AuthService: Validate request

alt User exist
     AuthService-->>DB: Lookup the user and status
     alt email_verified==true && active==false
        AuthService->>Webclient: Reply: 401
        Webclient->>Webclient: Error popup
     end
else User does not exist
      AuthService-->>DB: Create user: email_verified is false, active is false
end
     AuthService-->AuthService: Generate JWT ID token and secret
     AuthService-->>Cache: Store secret
     AuthService-->>SMTP: Dispatch the secret
     AuthService->>Webclient: Response: user_id+secret expiration time
     SMTP->>User: Deliver email with the secret     
     User->>Webclient: Input secret
     Webclient-->Webclient: Validate secret's expiration
     Webclient->>AuthService: Call "/auth/confirm"
     AuthService-->>Cache: Lookup secret
     AuthService-->AuthService: Validate secret
     AuthService-->>DB: Update user: email_verified is true, active is true
     AuthService-->AuthService: Generate JWT: Access and Refresh tokens
     AuthService->>Webclient: Response: JWT tokens
     Webclient-->>Browser Cookies: Store JWT tokens

else Referesh token found
     Webclient->>AuthService: Call "/auth/refresh"
     AuthService-->AuthService: Validate token
     
     alt Token is invalid
       AuthService->>Webclient: Response: 401
       Webclient->>Webclient: Error popup, init the "login/sign-in flow"
    else Token is valid
       AuthService-->>DB: Lookup the user and status
      alt active==true
         AuthService-->AuthService: Generate JWT: Access tokens
         AuthService->>Webclient: Response: Send JWT tokens
         Webclient-->>Browser Cookies: Store JWT tokens
      else active==false
        AuthService->>Webclient: Response: 401
        Webclient->>Webclient: Error popup, init the "login/sign-in flow"          
       end
    end

end

Contract

/auth/signin/init

Request:

Method Schema
POST
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "/auth/email:request",
  "type": "object",
  "required": [
    "email"
  ],
  "additional_fields": false,
  "properties": {
    "email": {
      "title": "User's email",
      "type": "string",
      "pattern": "^[a-zA-Z0-9+_.-]+@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$"
    }
  }
}

Responses:

Status Schema Reason
200
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "/auth/email:response-200",
  "type": "object",
  "required": [
    "user_id",
    "exp"
  ],
  "additional_fields": false,
  "properties": {
    "user_id": {
      "title": "User ID",
      "type": "string",
      "format": "uuid"
    },
    "exp": {
      "title": "Timestamp when the secret expires",
      "type": "integer",
      "minimum": 0
    }
  }
}
Secret sent to user's email
400
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Error",
  "type": "object",
  "required": [
    "error"
  ],
  "additional_fields": false,
  "properties": {
    "error": {
      "title": "Error message",
      "type": "string"
    }
  }
}
Faulty input
401
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Error",
  "type": "object",
  "required": [
    "error"
  ],
  "additional_fields": false,
  "properties": {
    "error": {
      "title": "Error message",
      "type": "string"
    }
  }
}
User is deactivated
422
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Error",
  "type": "object",
  "required": [
    "error"
  ],
  "additional_fields": false,
  "properties": {
    "error": {
      "title": "Error message",
      "type": "string"
    }
  }
}
Faulty email, the secret cannot be sent
500
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Error",
  "type": "object",
  "required": [
    "error"
  ],
  "additional_fields": false,
  "properties": {
    "error": {
      "title": "Error message",
      "type": "string"
    }
  }
}
Internal server error

/auth/anonym

Request:

Method Schema
POST
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "/auth/anonym:request",
  "type": "object",
  "required": [
    "fingerprint"
  ],
  "additional_fields": false,
  "properties": {
    "fingerprint": {
      "title": "User's fingerprint",
      "type": "string",
      "pattern": "^[a-f0-9]{40}$"
    }
  }
}

Responses:

Status Schema Reason
200
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "/auth/anonym:response-200",
  "type": "object",
  "required": [
    "token_refresh",
    "token_access"
  ],
  "additional_fields": false,
  "properties": {
    "token_refresh": {
      "title": "JWT refresh token",
      "type": "object",
      "pattern": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$"
    },
    "token_access": {
      "title": "JWT access token",
      "type": "object",
      "pattern": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$"
    }
  }
}
OK
400
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Error",
  "type": "object",
  "required": [
    "error"
  ],
  "additional_fields": false,
  "properties": {
    "error": {
      "title": "Error message",
      "type": "string"
    }
  }
}
Faulty input
500
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Error",
  "type": "object",
  "required": [
    "error"
  ],
  "additional_fields": false,
  "properties": {
    "error": {
      "title": "Error message",
      "type": "string"
    }
  }
}
Internal server error

/auth/signin/confirm

Request:

Method Schema
POST
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "/auth/signin/confirm:request",
  "type": "object",
  "required": [
    "user_id",
    "secret"
  ],
  "additional_fields": false,
  "properties": {
    "user_id": {
      "title": "User ID",
      "type": "string",
      "format": "uuid"
    },
    "secret": {
      "title": "Authentication secret",
      "type": "string",
      "pattern": "^[0-9]{6}$"
    }
  }
}

Responses:

Status Schema Reason
200
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "/auth/confirm:response-200",
  "type": "object",
  "required": [
    "token_refresh",
    "token_access"
  ],
  "additional_fields": false,
  "properties": {
    "token_refresh": {
      "title": "JWT refresh token",
      "type": "string",
      "pattern": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$"
    },
    "token_access": {
      "title": "JWT access token",
      "type": "string",
      "pattern": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$"
    }
  }
}
OK
400
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Error",
  "type": "object",
  "required": [
    "error"
  ],
  "additional_fields": false,
  "properties": {
    "error": {
      "title": "Error message",
      "type": "string"
    }
  }
}
Faulty input
401
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Error",
  "type": "object",
  "required": [
    "error"
  ],
  "additional_fields": false,
  "properties": {
    "error": {
      "title": "Error message",
      "type": "string"
    }
  }
}
Unauthorized
500
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Error",
  "type": "object",
  "required": [
    "error"
  ],
  "additional_fields": false,
  "properties": {
    "error": {
      "title": "Error message",
      "type": "string"
    }
  }
}
Internal server error

/auth/refresh

Request:

Method Schema
POST
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "/auth/refresh:request",
  "type": "object",
  "required": [
    "token_refresh"
  ],
  "additional_fields": false,
  "properties": {
    "user_id": {
      "title": "JWT refresh token",
      "type": "string",
      "format": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$"
    }
  }
}

Responses:

Status Schema Reason
200
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "/auth/refresh:response-200",
  "type": "object",
  "required": [
    "token_access"
  ],
  "additional_fields": false,
  "properties": {
    "token_access": {
      "title": "JWT access token",
      "type": "object",
      "pattern": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$"
    }
  }
}
OK
400
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Error",
  "type": "object",
  "required": [
    "error"
  ],
  "additional_fields": false,
  "properties": {
    "error": {
      "title": "Error message",
      "type": "string"
    }
  }
}
Faulty input
401
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Error",
  "type": "object",
  "required": [
    "error"
  ],
  "additional_fields": false,
  "properties": {
    "error": {
      "title": "Error message",
      "type": "string"
    }
  }
}
Unauthorized
500
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Error",
  "type": "object",
  "required": [
    "error"
  ],
  "additional_fields": false,
  "properties": {
    "error": {
      "title": "Error message",
      "type": "string"
    }
  }
}
Internal server error

Add web client's logic to invoke the backend

Problem

Web client shall invoke the server logic.

Proposed Solution

  • Add logic to handle onclick event of the button "Generate Diagram" to call backend. The logic:
    • Reads textarea input
    • Validates it for length: between 3 and 700 symbols
    • Call backend endpoint using fetch:
      • method: POST
      • payload: {"prompt" "{{.input}}"}
      • endpoint: /generate

image

DoD

  • Client logic v0.0.1 is released

As a user, I want to have access to diagram as code

Problem

As a user, I want to have access to diagram as code

Proposed Solution

  • Add logic to display and download the plantuml code used to generate resulting diagram.
  • Add button to download the code as diagramascode.plantuml file

Add authN layer

Context

As a user, I want to have access to better model(s) and advanced features.

  • Fine-tuned models are paid, hence an "authentication wall" filter shall be set to keep the costs under control
  • Non-guest users data shall be used to improve the product quality

Problem

Authentication layer is required.

Proposed Solution

Implement pass-wordless authentication using Auth0:

  • email with an auth code
  • 3rd party IdPs:
    • GitHub
    • Google

Note that we maintain the option for a user to assume the "guest role", i.e. no authentication.

Flowchart

flowchart LR
    subgraph App
        trigger[Button 'Generate Diagram'] --> cache
        cache --> cacheGuest{Guest prefernece}
        cacheGuest -- No --> cacheAuth{JWT found}
        download[Button 'Download'] --> diagram[Diagram SVG]
    end
    subgraph Auth[Authentication Service]
        authFlow{Is IdP?}
        authFlow -- No --> email
        email[Email flow] --> SMTP
        email --> Logic
        Logic --> db[(Userbase)]
    end

    user((User)) --> trigger
    user --Download generated diagram--> download
    server --> diagram
    cacheGuest -- Yes --> server[Server: Core]
    cacheAuth --Yes--> server
    cacheAuth --No--> Logic
    user -- Authenticates --> authFlow
    user -- Enters email --> email
    user -- Enters access code --> Logic
    SMTP -- Sends access code --> userEmail(((User email server)))
    user -- Fetch access code --> userEmail
    Logic -- Returns JWT --> cache
    authFlow -- Yes --> IdP(((3rd party IdP)))
    user -- Confirms usage --> IdP
    IdP -- Configrms authentication --> Logic

### References

- Auth0
- API GW AuthN
- webclient logic with vanilla js:
~~ - auth0 docu~~
~~ - auth0 user's blog~~

Acceptance Criteria

  • Users are prompted to login screen upon the first click of the button "Generate Diagram"
  • Authenticated user's ID is propagated to the backend
    - Auth0 authN is plugged into the API GW
  • API GW request cognito authorisation is set

As a user, I want the service to be not disrupted by discontinuity of the Codex API support by OpenAI

Problem

Codex API will be discontinued on 2023-03-23.

The announcement > On March 23rd, we will discontinue support for the Codex API. All customers will have to transition to a different model. Codex was initially introduced as a free limited beta in 2021, and has maintained that status to date. Given the advancements of our newest GPT-3.5 models for coding tasks, we will no longer be supporting Codex and encourage all customers to transition to GPT-3.5-Turbo. > About GPT-3.5-Turbo > GPT-3.5-Turbo is the most cost effective and performant model in the GPT-3.5 family. It can both do coding tasks while also being complemented with flexible natural language capabilities. > You can learn more through: [GPT-3.5 model overview](http://url3243.email.openai.com/ls/click?upn=8NGuCp9HhBmIwvt7K-2Bq2nILX-2B5rHUfYyvCd2ZUkBec-2BUBYsPWlBIv6BazDIaay-2FxzhxxDEdA6Vw5PgW3IRu1RA-3D-3DvUUs_r-2BDMsuWuYVmb7-2F1Mu1QL1NDxONgfEpVZL85Lxjq3DbbwFuttcppHMpklQe6-2FI-2FE63i5U9XpVPVayZ-2FANk8ndDPIh34R85g7SYDpFzYNHbMQUTG2aPeaBmZR38Xb3jbMVzboVwoSTRGVYVxTavB2y8tsO1GARKlwLfJvXUg2WvtKx4PuSD2MBv8Xtu4-2FtaxuK2d0iblCAWx3ouFbTb26SZCVg4y-2B4Y9gIQGPqVUQjEwBbY-2FBOSc8nSeJ6rpnnHFxZ-2FsDkWIkmlVx6I-2B1kUUzlSWxDhirr1lP2tpufOjTvXTwh84iiJdjwj8Cvk6Ksxxe02W84Gc-2F9UZq71N5pNViXc3-2BpUo5cd3Gdlq-2FOQBzw4LKyb2njbqG65uK82LHD6UogyML-2FqhzSruSUPrelpJbTWXPF38MqZ1eUyGyM9Ci1kbZQ3Pv-2FpuY9fv-2F6FW-2Byaogc0lvlPvaGeksp8wU8uYJdT3xPjPY5i49qx-2BlbqdluXxDEzcBBK1hVr0o3wlcgGEV2T9UCDAJe1m5dSM6q2GMRjMbPhZYpKqAeiyjai9ihde0e-2BUi83nJHQoq5yYHLg2Yeg70YPapc-2BTYUhg-2BBtACGtGJz2iQVRzkqh8uXU0RYAl9Mftvygy7HU8KaWqAkPmFoNPfKa-2BIBDdT4AWnSQaoibD-2B9GNx754EfyZfYPMkOK1FvSQnMg9JwYRWum76WzyYK8TgEmaFqSA6PvuWxzHVHLCei0sPRAPlBGMawnub9s9SRtbMocvgYBwklqSbhB4AA8-2FaE5ekUkaMw1s2Oy8zRDnDVU4qcY6uFoW-2BcqGXm47C9clMzBU9FJWrbK2Gcd7P53VSxali1gPBqN9YaY-2FlqvHwYCQeOL3kUgmPHGsV94rBBoMs7ASv6FuYkGGUYxySix2L3TxdCo77yDq5iqKAHywmBUd6Od7IS8KfYT2ceOtk-3D) [Chat completions guide](http://url3243.email.openai.com/ls/click?upn=8NGuCp9HhBmIwvt7K-2Bq2nILX-2B5rHUfYyvCd2ZUkBec819gUbMOcpnK5enS3RP52OU9rrk0-2FA-2F2UuiMfrkPSAdQ-3D-3DbWGb_r-2BDMsuWuYVmb7-2F1Mu1QL1NDxONgfEpVZL85Lxjq3DbbwFuttcppHMpklQe6-2FI-2FE63i5U9XpVPVayZ-2FANk8ndDPIh34R85g7SYDpFzYNHbMQUTG2aPeaBmZR38Xb3jbMVzboVwoSTRGVYVxTavB2y8tsO1GARKlwLfJvXUg2WvtKx4PuSD2MBv8Xtu4-2FtaxuK2d0iblCAWx3ouFbTb26SZCVg4y-2B4Y9gIQGPqVUQjEwBbY-2FBOSc8nSeJ6rpnnHFxZ-2FsDkWIkmlVx6I-2B1kUUzlSWxDhirr1lP2tpufOjTvXTwh84iiJdjwj8Cvk6Ksxxe02W84Gc-2F9UZq71N5pNViXc3-2BpUo5cd3Gdlq-2FOQBzw4LKyb2njbqG65uK82LHD6UogyML-2FqhzSruSUPrelpJbTWXPF38MqZ1eUyGyM9Ci1kbZQ3Pv-2FpuY9fv-2F6FW-2Byaogc0lvlPvaGeksp8wU8uYJdT3xPjPY5i49qx-2BlbqdluXxDEzcBBK1hVr0o3wlcgGEV2T9UCDAJe1m5dSM6q2GMRjMbPhZYpKqAeiyjai9ihde0e-2BUi83nJHQoq5yYHLg2Yeg70YPapc-2BTYUhg-2BBtACGtGJz2iQVRzkqh8uXU0RYAl9Mftvygy7HU8KaWqAkPmFoNPfKa-2BIBDdT4AWnSQaoibD-2B9GNx754EfyZfYPMkOK1FvSQnMg9JwYRWum76WzyYK8TgEmaFqSA6PvuWxzHVHLGWz-2BD03Jb9j7bf0jYzPfzzF5xmBDNq6D8o45b4s-2BdGm4i2V3xt1cEGtOMgtD-2FT0UCvIfJ5e58D2PWCI5AFkRnvwY23TGD6m8xbBXZYSfuGZGMmjfc8msW9SlK5p5lJd448CzO6raWuoIx6WZnb1qQgVx3Ym-2FjIdHlkIdTW3HnXyzxx24bDgEUkL1Vgfqhsny5jy2l66MW3Psf8Mp4eZ4OA-3D) > Models affected > The following models will be discontinued: > code-cushman:001 > code-cushman:002 > code-davinci:001 > code-davinci:002 > We understand this transition may be temporarily inconvenient, but we are confident it will allow us to increase our investment in our latest and most capable models. > β€”The OpenAI team

Proposed solution

Adjust the c4diagram logic to rely on GPT-3.5-Turbo.

Acceptance Criteria

  • Service is active post 2023-03-23 after the codex model is discontinued

Migrate from Neon to Aurora

Problem

@deim and I discovered that Neon connection goes down for unknown reasons during pairing session.

Proposed Solution

Migrate to AWS RDS Aurora

As a maintainer, I want to have intel on daily usage of the product

Problem

As @kislerdm, I need to check the cloudwatch dashboard to know dynamics of the app usage:

image

Proposed Solution

Fetch metrics on a daily basis and share them in the team's Slack. Github actions triggered on schedule will be used. awscli will used.

  • Define CI job
    • Setup AWS auth credentials available in the production environment
    • Setup Slack API token in the production environment
      • Create a slack app

Acceptance Criteria

  • The usage metrics are available in the Slack channel product-analytics:
    • Total number of daily requests on the date
    • Total number of generated diagrams on the date
  • Metrics are published daily at 9AM CET

As a service provider, I want to maintain security standards

Problem

Webclient deployment exposes the api key required to communicate with the server. It is caused by the the github pages setup, i.e. the artifact is stored to gh-pages branch.

Proposed Solutions

Make API public

Pros

  • Simplicity

Cons

  • None, given that the app is publicly available and that API quota is maintained

Deploy github pages using artifacts instead of pages

Pros

  • No secrets exposure in the codebase

Cons

  • ?

Add auth layer

Pros

  • Granular users tracking

Cons

  • None. However, a secret would be required to be propagated to the client to communicate with authN layer, e.g. auth0.

Add secretsmanager to maintain access credentials

Problem

Core logic v0.0.1 uses secrets from env. variables which implies security risks.

Proposed Solution

The logic to fetch secrets from AWS Secretsmanager is implemented as part of the lambda entrypoint logic.

DoD

  • The module github.com/kislerdm/diagramastext/core/cmd/lambda is developed
  • The module v0.0.x is released

Add smoke test

GIVEN

Server and client setup provisioned, and apps deployed and configured

WHEN

Train openAI curie model using the data sample of 20 elements

Problem

As a user, I want high quality translation of my instructions.

Proposed Solution

Followup on #9 and train fune-tuned model using 20 elements: 10 existing as the result of #9 and 10 additional.

DoD

  • The model is successfully trained

Acceptance Criteria

  • The model with the new prefix (commit sha) is available for inference
  • The model performance, i.e. training metrics are noted

Add AWS Lambda interface to map HTTP requests to `Handler`'s methods

Problem

Core logic shall be invoked over network.

Proposed Solution

  • Map HTTP requests to procedure calls to invoke the Handler's methods
  • Use the AWS Lambda HTTP mapping to propagate the event content to the Handler
  • The lambda will be adopted to interface AWS Gateway
  • The secrets are fetched from the environment variables.

DoD

  • The module github.com/kislerdm/diagramastext/core/cmd/lambda is developed
  • The module relies on github.com/kislerdm/diagramastext/core
  • Test coverage coverage is at least 80%

As a user, I want to access the service using Rest API

Problem

There is a use case to employ the core logic over HTTP API.

Proposed Solution

Provide access to the core logic over HTTP API.

AuthN/Z

Header: Authorization: Bearer {{Token}}

Acceptance Criteria

  • Core logic is accessible over HTTP using Rest API
  • API contract is available to the user

Train openAI curie model using the data sample of at least a 100 elements

Problem

OpenAI provides LLMs which however do not perform well when it comes to text2diagram.

Proposed Solution

Perform fine-tuning of the curie model using the data sample of 100+ elements:

  • Training sample of 100+ data points will be collected as json file with the structure:
[{
     "prompt": "three connected boxes",
     "completion": {"nodes":[{"id":"0"},{"id":"1"},{"id":"2"}],"links":[{"from":"0","to":"1"},{"from":"1","to":"2"},{"from":"2","to":"0"}]}
},
...
]
  • The training will be submitted following the instructions. See the reference for details.
  • The resulting model will have the suffix of the commit sha corresponding to the *.py file with the training data sample.

DoD

  • The model is successfully trained

Acceptance Criteria

  • The model is available for inference
  • The model performance, i.e. training metrics are noted

Setup alternative model

It may be beneficial to consider a switch to one of recently published facebook models.

Note that it's a shapeup/experimental task.

As app maintainer, I want to be alerted about app behaviour incidents

Problem

The app is running "in a wild" without alerting system.

Proposed Solution

Set cloudwatch alerts:

  • the ratio of failed requests to the total number of requests: over 10%

Acceptance Criteria

  • Maintainers are notified in slack about alters in the channel alerts-producation

Fix the downloadable SVG file of a diagram

Environment

Prompt

the banking application that supports not only a mobile app, but also a single-page application. We also have an e-mail system and it's all supported via our Bank API that is connected to the database (we use Oracle)

Details

  1. Copy/paste a prompt
  2. Click "Generate a diagram"
  3. Diagramastext displays a diagram
  4. Click "Download"
  5. Open a diagram.svg file in a browser
  6. See it being broken, with no diagram's elements

diagram.svg contains this:

<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="458px" preserveAspectRatio="none" style="width:352px;height:458px;background:

Expected behaviour

  1. The downloaded SVG file is filled with the diagram's elements and can be opened in the browser to display an actual diagram

Add logic to convert graph to plantuml code

User's input is "translated" to the DiagramGraph object with AI; it shall be further converted to "diagram as code" according to the planuml API to generate resulting artifact, i.e. svg image.

Import users' generated data from cloud watch to the Neon db

Problem

Users generated the data very valuable for model improvement. The data generated before 2023-02-16 are stored to cloudwatch instead of the core db.

Proposed Solution

  • Create the s3 bucket dev.diagramastext.data
  • Dump the cloudwatch the lambda core-c4 logs to the bucket with the prefix logsdump/lambda/
  • Parse the logs and extract the prompt and response
    - [ ] Import parsed data to the Neon database core on the basis of #41

Parsing details

  • Use "00000000-0000-0000-0000-000000000000" as the placeholder for user_id value. The rational: the user is not set on the client side yet.
  • Use lambda request_id as the value for the tables' field request_id.
  • Database contract:
    • user_prompt DDL
    • openai_response DDL

Example

GIVEN

The log input:

2023-02-14T23:07:04.021Z INIT_START Runtime Version: go:1.v13	Runtime Version ARN: arn:aws:lambda:us-east-2::runtime:3f96445bbbd5587be5ace0de37b48e731074fa350870a8a3cb87f45011d70281

2023-02-14T23:07:04.105Z START RequestId: a54aa4b0-744d-4e1f-91d8-7eb26f99dd6f Version: $LATEST

2023-02-14T23:07:04.106Z 2023/02/14 23:07:04 {"prompt":"C4 diagram of a Go web server reading from external Postgres database over TCP without the big part"}

2023-02-14T23:07:05.576Z 2023/02/14 23:07:05 {"raw":"{"id":"cmpl-6jyg4CeNcXH7dDfOrbWFKYFuBIfVu","object":"text_completion","created":1676416024,"model":"code-cushman-001","choices":[{"text":"{\"nodes\":[{\"id\":\"0\",\"label\":\"Web Server\",\"technology\":\"Go\",\"description\":\"Authenticates users\"},{\"id\":\"1\",\"label\":\"Database\",\"technology\":\"Postgres\",\"external\":true,\"is_database\":true}],\"links\":[{\"from\":\"0\",\"to\":\"1\",\"label\":\"reads from database\",\"technology\":\"TCP\"}]}","index":0,"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":842,"completion_tokens":210,"total_tokens":1052}}

2023-02-14T23:07:05.576Z "}

2023-02-14T23:07:05.576Z 2023/02/14 23:07:05 {"cleaned":"{"nodes":[{"id":"0","label":"Web Server","technology":"Go","description":"Authenticates users"},{"id":"1","label":"Database","technology":"Postgres","external":true,"is_database":true}],"links":[{"from":"0","to":"1","label":"reads from database","technology":"TCP"}]}"}

2023-02-14T23:07:06.987Z END RequestId: a54aa4b0-744d-4e1f-91d8-7eb26f99dd6f

2023-02-14T23:07:06.987Z REPORT RequestId: a54aa4b0-744d-4e1f-91d8-7eb26f99dd6f	Duration: 2882.69 ms	Billed Duration: 2883 ms	Memory Size: 256 MB	Max Memory Used: 39 MB	Init Duration: 82.83 ms

WHEN

Apply parsing logic - TBD in terms of the task

THEN

The extracted values are expected to correspond to the following fields of db tables.

user_prompt:

request_id user_id prompt timestamp
a54aa4b0-744d-4e1f-91d8-7eb26f99dd6f 00000000-0000-0000-0000-000000000000 C4 diagram of a Go web server reading from external Postgres database over TCP without the big part 2023-02-14T23:07:04.106Z 2023/02/14 23:07:04

openai_response:

request_id user_id response timestamp
a54aa4b0-744d-4e1f-91d8-7eb26f99dd6f 00000000-0000-0000-0000-000000000000 {"nodes":[{"id":"0","label":"Web Server","technology":"Go","description":"Authenticates users"},{"id":"1","label":"Database","technology":"Postgres","external":true,"is_database":true}],"links":[{"from":"0","to":"1","label":"reads from database","technology":"TCP"}]} 2023-02-14T23:07:05.576Z 2023/02/14 23:07:05

Acceptance Criteria

  • All lambda logs until 2023-02-16 inclusive are parsed
  • Parsing results are stored to the core database, tables user_prompt and openai_response

Add PlantUML interface

Problem

Generated diagram instructions shall be converted to the svg-encoded diagram using PlantUML endpoint http://www.plantuml.com/plantuml/svg/{{.diagramEncodedString}}

Proposed Solution

Develop the ClientPlantUML to communicate with PlantUML over HTTP

DoD

  • The logic is implemented in the plantuml.go
  • Test coverage is at least 80%
  • The core module v0.0.2 is released

Add "entrypoint" interface to the core logic

Problem

The user's input shall be handled by the code logic according to the diagram below.

diagram

Proposed Solution

  • The Handler interface with the following public methods:
    • GenerateDiagram(string) (ResponseDiagram, error)
  • The Handler is configured with ClientPlantUML and ClientOpenAI

Acceptance Criteria

  • The logic is implemented in handler.go
  • Test coverage is at least 80%

As the service provider, I want to sustain requests throttling quota

Problem

Throttling is not controlled, hence we risk to hit the OpenAI quotas limits and affect clients.

Proposed Solution

  • Keep track of the the user's session
  • Limit the rate:
    • 1 request per minute for a non-registered user
    • 3 requests per minute for a registered user

A user's fingerprint is defined as a result of hash function applied to

  • the user-agent string for non-registered users;
  • the user's email for registered users.

The fingerprint is stored as a custom JWT token's attribute.

Acceptance Criteria

  • Throttling limit is applied

DoD

  • Core logic caches the following:
    • Call's timestamp
    • Caller's fingerprint
    • Caller's registration status
  • The in-memory cache is backed by gcs

Restrict permission for github runner's AWS role

Tech debt collected during execution of #35, it's a potential security threat:

resource "aws_iam_role_policy_attachment" "admin" {
  # FIXME: grant admin right on exceptional basis
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
  role       = aws_iam_role.github_actions.arn
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.