Compare commits

..
814 Commits
Author SHA1 Message Date
Danny AvilaandGitHub 164aad12f4 Release 0.4.2 (#242)
* release: 0.4.2

* docs: update changelog and readme for v0.4.2 release

* docs(README.md): add important information about new user system and env variables
2023-05-11 16:39:44 -04:00
Danny AvilaandGitHub 676ab04d96 fix(docker): update .dockerignore to include client/.env file (#241)
fix(docker): add COPY command to copy client/.env file into the container before building
2023-05-11 15:59:43 -04:00
c88f0c43b6 Feature/logging system with pino and sanitization (#214) (#227)
* feat: Create structured data logging system with Pino

This commit creates a new feature that enables structured data logging using the Pino logging library. The structured data logging feature allows for more granular and customizable logging, making it easier to analyze and debug issues in the application.

The changes made in this commit include:
- Adding support for structured data logging using the Pino API
- Adding support to redact sensible data from logging output using pino
  redact.
- Pino integrate natively with fluentd, logstash, Docker Logging Drivers
  and other JSON based system.

* Add pino package to project

* Logging-System: Add support for an array of regex to redact

* Logging-Systems: Add Redact Patterns and Pino Redact Paths + Boolean logics wasn't right.

Co-authored-by: Olivier Contant <ocontant@users.noreply.github.com>
2023-05-10 23:59:26 -04:00
Danny AvilaandGitHub 037883d6ef refactor(titleConvo.js, endpoints.js): add support for AZURE_OPENAI_API_KEY environment variable (#235) 2023-05-10 23:56:24 -04:00
Danny AvilaandGitHub 3ce17b086c feat(chatgpt-client.js, titleConvo.js, genAzureEndpoints.js): add support for Azure OpenAI API endpoint generation (#234)
This commit adds support for generating Azure OpenAI API endpoints in the
`chatgpt-client.js` and `titleConvo.js` files. The `genAzureEndpoint` function
in `genAzureEndpoints.js` generates the endpoint URL based on the provided
parameters. The `chatgpt-client.js` and `titleConvo.js` files now use this
function to generate the endpoint URL when the `AZURE_OPENAI_API_KEY` environment
variable is set.
2023-05-10 23:47:26 -04:00
YouthandGitHub 73a5ea467d * refactor(getCitations.js): add null check for adaptiveCards variable before accessing its properties (#232) 2023-05-10 21:23:15 -04:00
FuegovicandGitHub ddfc4644ae Update README.md to v0.4.1 (#224)
* Update CONTRIBUTORS.md

* Update CHANGELOG.md

v0.4.1 Changelog

v0.4.1 changelog and link
Contributors in the TOC

* Update README.md

add a link to the alternative docs from @DavidDev1334

* Update linux_install.md

Credit @DavidDev1334 for linux install doc
2023-05-09 22:12:12 -04:00
Danny AvilaandGitHub 5ec8ebe94b chore: bump version to 0.4.1 in package.json files (#222)
feat: update docker-compose.yml to use latest image from docker hub
2023-05-09 17:57:04 -04:00
9a81578d8a Update Message.js (#191)
Fixed Error handling, Code duplication and Naming conventions. Contact me for more information at: DavidTheDev#0166

Co-authored-by: Danny Avila <110412045+danny-avila@users.noreply.github.com>
2023-05-09 17:51:39 -04:00
Danny AvilaandGitHub 761223aa7a Feat: Add Azure support (#219)
feat(api): add support for Azure OpenAI API

- Add Azure OpenAI API environment variables to .env.example
- Modify chatgpt-client.js to use Azure OpenAI API if environment variables are present
- Modify askOpenAI.js to use arrow function syntax
- Modify handlers.js to add console.log statement for partial variable
2023-05-09 17:42:55 -04:00
FuegovicandGitHub 480680864f update documentation structure (#220)
* documentation refactor

* Update README.md

* Delete README.MD.md

* Delete LOCAL_INSTALL.md

* Rename LICENSE.MD.md to LICENSE.MD

* Update LICENSE.md

* Delete LICENSE.MD

* Rename CONTRIBUTORS.MD.md to CONTRIBUTORS.md

* Rename CHANGELOG.MD.md to CHANGELOG.md

* new documents layout

* Update README.md

* Rename mac_install (1).md to mac_install.md

* Rename docker_install.md to docker_install.md

* Rename linux_install.md to linux_install.md

* Update and rename mac_install.md to mac_install.md

* Rename windows_install.md to windows_install.md

* Update docker_install.md

* Update linux_install.md

* Update mac_install.md

* Update windows_install.md

* Update windows_install.md

* Update linux_install.md

* Update tech_stack.md

* Update roadmap.md

* Update project_origin.md

* Update bing_jailbreak_info.md

* Update user_auth_system.md

* Update proxy.md

* Update google_search.md

* Update heroku.md

* Update testing.md

* Update pull_request_template.md

* Update documentation_guidelines.md

* Update contributor_guidelines.md

* Update code_of_conduct.md

* Update README.md

* Update README.md

* Update README.md

* Update roadmap.md

* Update tech_stack.md

* Update feature_request_template.md

* Update bug_report_template.md

* Update custom_issue_template.md

* Update README.md

fix redirect

* Update README.md

dynamic toc

* Update README.md

hide plugins section for now

* Update README.md

removed plugins from TOC

* Update README.md

* Update README.md

* Update documentation_guidelines.md

* Update documentation_guidelines.md

* Update documentation_guidelines.md

directives update

* Update README.md

update shortcut

* Update CHANGELOG.md

* Update roadmap.md

add public trello link

* Update linux_install.md
2023-05-09 13:47:14 -04:00
Danny AvilaandGitHub c2bd510274 chore: update docker image version to 0.4.0 (#218)
* chore(docker-compose.yml): update docker-compose file to use local node-api image build instead of docker hub image build

* chore(docker-compose.yml): update docker image version to 0.4.0
2023-05-08 18:57:30 -04:00
Dan OrlandoandGitHub 82a899cbfb fix issue with validation when google account has multiple spaces in name (#211) 2023-05-07 20:31:27 -04:00
Dan OrlandoandGitHub 764100026d fix browser refresh redirecting to /chat/new (#210) 2023-05-07 19:16:12 -04:00
FuegovicandGitHub 1730b0a0dd Update README.md (#209)
Fix typos in the google login setup instruction and added the update info in the update section of the readme
2023-05-07 19:11:00 -04:00
Dan OrlandoandGitHub 376f23b494 remove github-passport and update package.lock files (#208) 2023-05-07 16:22:13 -04:00
Dan OrlandoandGitHub 76ccf15a5b update user system section of readme (#207) 2023-05-07 15:51:18 -04:00
Dan OrlandoandGitHub 4c72d01388 Bump package version and fix spacing in user system section of readme (#206) 2023-05-07 15:27:04 -04:00
2bb98c9eba feat: Auth and User System (#205)
* server-side JWT auth implementation

* move oauth routes and strategies, fix bugs

* backend modifications for wiring up the frontend login and reg forms

* Add frontend data services for login and registration

* Add login and registration forms

* Implment auth context, functional client side auth

* protect routes with jwt auth

* finish local strategy (using local storage)

* Start setting up google auth

* disable token refresh, remove old auth middleware

* refactor client, add ApiErrorBoundary context

* disable google and facebook strategies

* fix: fix presets not displaying specific to user

* fix: fix issue with browser refresh

* fix: casing issue with User.js (#11)

* delete user.js to be renamed

* fix: fix casing issue with User.js

* comment out api error watcher temporarily

* fix: issue with api error watcher (#12)

* delete user.js to be renamed

* fix: fix casing issue with User.js

* comment out api error watcher temporarily

* feat: add google auth social login

* fix: make google login url dynamic based on dev/prod

* fix: bug where UI is briefly displayed before redirecting to login

* fix: fix cookie expires value for local auth

* Update README.md

* Update LOCAL_INSTALL structure

* Add local testing instructions

* Only load google strategy if client id and secret are provided

* Update .env.example files with new params

* fix issue with not redirecting to register form

* only show google login button if value is set in .env

* cleanup log messages

* Add label to button for google login on login form

* doc: fix client/server url values in .env.example

* feat: add error message details to registration failure

* Restore preventing paste on confirm password

* auto-login user after registering

* feat: forgot password (#24)

* make login/reg pages look like openai's

* add password reset data services

* new form designs similar to openai, add password reset pages

* add api's for password reset

* email utils for password reset

* remove bcrypt salt rounds from process.env

* refactor: restructure api auth code, consolidate routes (#25)

* add api's for password reset

* remove bcrypt salt rounds from process.env

* refactor: consolidate auth routes, use controller pattern

* refactor: code cleanup

* feat: migrate data to first user (#26)

* refactor: use /api for auth routes

* fix: use user id instead of username

* feat: migrate data to first user on register

* fix: fix social login routes after refactor (#27)

* refactor: use /api for auth routes

* fix: use user id instead of username

* feat: migrate data to first user on register

* fix: fix social login routes

* fix: issue with auto-login when logging out then logging in with new browser window (#28)

* refactor: use /api for auth routes

* fix: use user id instead of username

* feat: migrate data to first user on register

* fix: fix social login routes

* fix: fix issue with auto-login in new tab

* doc: Update README and .env.example files with user system information (#29)

* refactor: use /api for auth routes

* fix: use user id instead of username

* feat: migrate data to first user on register

* fix: fix social login routes

* fix: fix issue with auto-login in new tab

* doc: update README and .env.example files

* Fixup: LOCAL_INSTALL.md PS instructions (#200) (#30)

Co-authored-by: alfredo-f <alfredo.fomitchenko@mail.polimi.it>

* feat: send user with completion to protect against abuse (#31)

* Fixup: LOCAL_INSTALL.md PS instructions (#200)

* server-side JWT auth implementation

* move oauth routes and strategies, fix bugs

* backend modifications for wiring up the frontend login and reg forms

* Add frontend data services for login and registration

* Add login and registration forms

* Implment auth context, functional client side auth

* protect routes with jwt auth

* finish local strategy (using local storage)

* Start setting up google auth

* disable token refresh, remove old auth middleware

* refactor client, add ApiErrorBoundary context

* disable google and facebook strategies

* fix: fix presets not displaying specific to user

* fix: fix issue with browser refresh

* fix: casing issue with User.js (#11)

* delete user.js to be renamed

* fix: fix casing issue with User.js

* comment out api error watcher temporarily

* feat: add google auth social login

* fix: make google login url dynamic based on dev/prod

* fix: bug where UI is briefly displayed before redirecting to login

* fix: fix cookie expires value for local auth

* Only load google strategy if client id and secret are provided

* Update .env.example files with new params

* fix issue with not redirecting to register form

* only show google login button if value is set in .env

* cleanup log messages

* Add label to button for google login on login form

* doc: fix client/server url values in .env.example

* feat: add error message details to registration failure

* Restore preventing paste on confirm password

* auto-login user after registering

* feat: forgot password (#24)

* make login/reg pages look like openai's

* add password reset data services

* new form designs similar to openai, add password reset pages

* add api's for password reset

* email utils for password reset

* remove bcrypt salt rounds from process.env

* refactor: restructure api auth code, consolidate routes (#25)

* add api's for password reset

* remove bcrypt salt rounds from process.env

* refactor: consolidate auth routes, use controller pattern

* refactor: code cleanup

* feat: migrate data to first user (#26)

* refactor: use /api for auth routes

* fix: use user id instead of username

* feat: migrate data to first user on register

* fix: fix social login routes after refactor (#27)

* refactor: use /api for auth routes

* fix: use user id instead of username

* feat: migrate data to first user on register

* fix: fix social login routes

* fix: issue with auto-login when logging out then logging in with new browser window (#28)

* refactor: use /api for auth routes

* fix: use user id instead of username

* feat: migrate data to first user on register

* fix: fix social login routes

* fix: fix issue with auto-login in new tab

* doc: Update README and .env.example files with user system information (#29)

* refactor: use /api for auth routes

* fix: use user id instead of username

* feat: migrate data to first user on register

* fix: fix social login routes

* fix: fix issue with auto-login in new tab

* doc: update README and .env.example files

* Send user id to openai to protect against abuse

* add meilisearch to gitignore

* Remove webpack

---------

Co-authored-by: alfredo-f <alfredo.fomitchenko@mail.polimi.it>

---------

Co-authored-by: Danny Avila <110412045+danny-avila@users.noreply.github.com>
Co-authored-by: Alfredo Fomitchenko <alfredo.fomitchenko@mail.polimi.it>
2023-05-07 10:04:51 -07:00
alfredo-fandGitHub 795a4ff4dc Fixup: LOCAL_INSTALL.md PS instructions (#200) 2023-05-03 07:16:38 -04:00
Danny AvilaandGitHub 5c65f39d4e Merge pull request #196 from alfredo-f/alfredo/playwright
Add instructions for local testing.
2023-04-30 06:54:45 -04:00
Alfredo Fomitchenko b119c9b8c4 Add local testing instructions 2023-04-30 08:53:30 +02:00
Danny AvilaandGitHub 56a8ac42c1 Merge pull request #195 from alfredo-f/alfredo/local_install
Update LOCAL_INSTALL structure
2023-04-29 16:34:22 -04:00
Alfredo Fomitchenko a72140ccd7 Update LOCAL_INSTALL structure 2023-04-29 21:13:33 +02:00
Danny AvilaandGitHub ffcf3cd1ee Update README.md 2023-04-28 08:15:35 -04:00
Danny AvilaandGitHub cb903d33d2 Merge pull request #188 from fuegovic/main
Added a link for the "automated installer"
2023-04-26 21:34:39 -04:00
FuegovicandGitHub 4c34cd752a Update README.md
layout fix for previous changes
2023-04-26 16:26:25 -04:00
FuegovicandGitHub cc26543c69 Update README.md
added information under the fuegovic's automated installer link
2023-04-26 16:24:37 -04:00
FuegovicandGitHub ea96b8029a Update LOCAL_INSTALL.md
Layout Updates
2023-04-26 16:21:36 -04:00
FuegovicandGitHub 77ee901fb9 Update README.md
added a link for fuegovic's automated installer
2023-04-26 16:17:46 -04:00
Danny AvilaandGitHub 7dd667c22b Merge pull request #187 from fuegovic/main
Update .env.example
2023-04-25 14:29:30 -04:00
FuegovicandGitHub f063851340 Update .env.example
Added clarification about the `user_provided` token use
2023-04-25 13:46:47 -04:00
FuegovicandGitHub 41fac2bb39 Update .env.example
added back the lines : # Set to "user_provided" to allow user provided token.
2023-04-25 13:09:24 -04:00
Danny AvilaandGitHub 5cbbbdc548 Merge pull request #186 from zhangsean/patch-1
Add container name
2023-04-25 10:00:09 -04:00
FuegovicandGitHub b0e3b1f9ae Update .env.example
I continued the work on the standardization of the layout. I also eliminated duplicate key=value pairs to simplify the configuration and reduce the likelihood of errors. I also updated some of the commented instructions and notes throughout the file to keep the instructions clear while making it easier to prevent errors when using a script to parse the key=value pairs.
2023-04-25 04:26:38 -04:00
Sean ZhangandGitHub d0fef7a360 Fix container name 2023-04-25 10:14:13 +08:00
Sean ZhangandGitHub 763e72a590 Add container name 2023-04-25 10:04:38 +08:00
Danny AvilaandGitHub 8fb19497aa Merge pull request #183 from danny-avila/fix-edit-wrap
add whitespace-pre-wrap to the message editor to preserve line breaks
2023-04-21 23:13:42 -04:00
Daniel Avila 1bd34188d5 style(Message.jsx): add whitespace-pre-wrap to the message editor to preserve line breaks 2023-04-21 23:12:45 -04:00
Danny Avila e60fd333d5 build(Dockerfile-app): add Dockerfile for building the app image
feat(docker-compose.yml): use pre-built image for api service instead of building from local file
2023-04-21 14:38:23 -04:00
Daniel Avila 7eb9217c8a fix(chatgpt-browser.js): update public reverse proxy URL 2023-04-20 13:13:47 -04:00
Danny AvilaandGitHub 73c154320d Update README.md 2023-04-16 23:12:59 -07:00
Danny AvilaandGitHub ea579e7eb9 Update README.md 2023-04-14 18:11:20 -07:00
Daniel Avila 1c024662fe feat(api): update @waylaidwanderer/chatgpt-api dependency to version 1.35.0 2023-04-12 05:47:10 -07:00
Danny AvilaandGitHub 4be33a9b66 Merge pull request #179 from danny-avila/fix-unknown-conversationId
fix: fix infinite query failure when conversationId is not found
2023-04-11 19:52:42 -06:00
Daniel Avila 69078eb8cf fix(api): correct typo in environment variable name from "user_provide" to "user_provided" in bingai.js and chatgpt-browser.js clients and endpoints.js router 2023-04-11 21:50:53 -04:00
Danny AvilaandGitHub a05f7f8579 Merge pull request #177 from danny-avila/user-providered-key
User providered key and unfinished messages.
2023-04-11 19:46:28 -06:00
Daniel Avila f39bdf30ea feat(Input/NewConversationMenu): save last selected model to localStorage
fix(getDefaultConversation.js): use last selected model from localStorage if available
2023-04-11 21:33:14 -04:00
Daniel Avila cf186c6490 refactor(tokenizer.js): remove console.log statement from tokenizer.js 2023-04-11 10:29:55 -04:00
Daniel Avila d342b6220a style(endpoints.js): fix indentation and add semicolons
fix(tokenizer.js): add try-catch block and error handling
style(SetTokenDialog/index.jsx): fix typo in sentence
refactor(data-service.ts): change argument format to match server API
2023-04-11 10:28:11 -04:00
Daniel Avila 2eca77f321 fix: only give initialResponse unfinished true value when not a cancellable endpoint 2023-04-10 18:27:06 -04:00
Daniel D Orlando 05d9f1553c fix: fix infinite query failure when conversationId is not found 2023-04-10 14:55:39 -07:00
Daniel Avila 8529d38345 refactor(Message.jsx): remove cancelled message bubble and improve wording of unfinished message 2023-04-10 17:15:28 -04:00
Daniel Avila 8d74b5b628 chore: bump version to 0.3.3 in package.json files 2023-04-10 17:06:49 -04:00
Daniel Avila e08eccaac7 fix(Input): fix isNotAppendable condition to include isSubmitting variable
fix(Input): prevent submitting message when isSubmitting is true and Enter key is pressed
2023-04-10 17:03:11 -04:00
JinruiandWentao Lyu e05a4f31c5 fix nginx container copying from wrong folder 2023-04-11 03:51:56 +08:00
Wentao Lyu fde919ec3f fix: typo 2023-04-11 03:50:37 +08:00
Wentao Lyu b878fb6628 mark initial response as unfinished. 2023-04-11 03:42:05 +08:00
Wentao Lyu 9bdb24f941 cleanup 2023-04-11 03:29:54 +08:00
Wentao Lyu cc6ec32220 merge from dannya
feat: support unfinished messages.
2023-04-11 03:26:38 +08:00
Wentao Lyu fc115e85be feat: support user-provided token to bingAI and chatgptBrowser 2023-04-10 14:51:38 +08:00
Daniel Avila 94a25ed149 wip: feat: abort messages and continue conversation
fix(addToCache.js): remove unused variables and parameters
feat(addToCache.js): add message to cache with id, parentMessageId, role, and text
fix(askOpenAI.js): remove parentMessageId parameter from addToCache call
feat(MessageHandler.jsx): add latestMessage to store on cancel of submission, and generate messageId and parentMessageId for latestMessage
2023-04-09 22:21:27 -04:00
Daniel Avila 4fd99422a8 feat(api): add support for saving messages to database
fix(api): change arrowParens prettier option to always
fix(api): update addToCache to include endpointOption and latestMessage
fix(api): update askOpenAI to include endpointOption in abortControllers
fix(client): remove abortKey state and add currentParent state to MessageHandler
2023-04-09 11:17:08 -04:00
Daniel Avila f4cfe40ceb wip: refactor: new abort message handling 2023-04-09 09:23:03 -04:00
Daniel Avila 2a70a7ff65 refactor(messageHandler): sends all necessary data to cache/save unfinished response 2023-04-09 09:22:14 -04:00
Daniel Avila 6cdc1d3ee6 feat(api-endpoints.ts): add abortRequest endpoint
feat(data-service.ts): add abortRequestWithMessage function
feat(react-query-service.ts): add useAbortRequestWithMessage hook
2023-04-09 09:21:04 -04:00
Daniel Avila 02e6a89ebd Merge branch 'main' into fix-sse 2023-04-09 07:54:06 -04:00
Daniel Avila 0bd9450572 fix(Messages/index.jsx): import lodash throttle function efficiently 2023-04-09 07:52:42 -04:00
Daniel Avila 33442b9f73 chore(package-lock.json): remove duplicate media-typer dependency and update its version to 0.3.0 in type-is and express dependencies. 2023-04-09 07:47:08 -04:00
Daniel Avila 120ab4afe4 Merge branch 'main' into fix-sse 2023-04-09 07:41:55 -04:00
Danny AvilaandGitHub 0484973ed6 Update README.md 2023-04-09 07:40:03 -04:00
Danny AvilaandGitHub d913edfe23 Update README.md 2023-04-09 07:39:41 -04:00
Danny AvilaandGitHub 46819ef1e8 Merge pull request #176 from danny-avila/minor-fixes
Minor fixes
2023-04-09 07:38:28 -04:00
Daniel Avila 50ec165f71 chore: update package versions to 0.3.2 2023-04-09 07:37:57 -04:00
Daniel Avila 942a85f531 fix: vite build issue: set memory limit for Node.js to 2048 MB 2023-04-09 07:35:10 -04:00
Daniel Avila 83d0443c8a fix: page no longer refreshes on stop generating button 2023-04-09 07:32:07 -04:00
Daniel Avila 88aea81288 WIP: fix: fix abort messages and continue conversation on abort
feat(askOpenAI.js): add abort endpoint to cancel requests
feat(MessageHandler): add abort functionality to cancel requests
feat(submission.js): add lastResponse and source atoms to store
feat(handleSubmit.js): add stopGenerating function to cancel requests
2023-04-08 23:19:29 -04:00
Danny AvilaandGitHub 5fbefa15ce Merge pull request #171 from git-bruh/main
fix: replace various anchor tags with buttons to prevent text selection on repeated clicks
2023-04-08 08:38:41 -04:00
Daniel Avila 0f62be812a refactor(AdjustToneButton.jsx): replace svg icon with lucide-react Settings2 icon 2023-04-08 08:37:44 -04:00
Danny AvilaandGitHub 92c1bb6511 Merge pull request #174 from HyunggyuJang/fix/reset-after-title-generation
Fix #119
2023-04-08 08:32:13 -04:00
Danny AvilaandGitHub cb56b74b0e Merge pull request #160 from HyunggyuJang/sydney/tone-adjustable
Make sydney tone adjustable during conversation
2023-04-08 08:30:05 -04:00
Daniel Avila 94078ce5d4 chore: remove GA until more tests are written 2023-04-08 08:21:34 -04:00
Hyunggyu Jang e5e4ee2987 Fix error 2023-04-08 13:32:15 +09:00
Hyunggyu Jang 100be3b42f Make sydney tone adjustable 2023-04-08 13:29:08 +09:00
Danny AvilaandGitHub 0a80f836f0 Merge pull request #165 from danny-avila/dano/react-query-typescript
Refactor: Create data-provider for api services with React Query and TypeScript
2023-04-07 22:14:44 -04:00
Daniel Avila 8b952be268 refactor(NewConversationMenu): remove unused imports and commented code
feat(NewConversationMenu): add support for deleting presets and creating new presets using data-provider
2023-04-07 22:11:28 -04:00
Daniel Avila 285351bb53 Merge branch 'main' into dano/react-query-typescript 2023-04-07 22:11:15 -04:00
Danny AvilaandGitHub 625f63b072 Merge pull request #173 from danny-avila/feat-new-titleconvo
Feat new titleconvo
2023-04-07 20:50:14 -04:00
Daniel D Orlando bf4258c0a5 Add react query devtools 2023-04-07 16:13:00 -07:00
Daniel D Orlando 9a0e3804fa fix nav pagination 2023-04-07 10:16:53 -07:00
Wentao Lyu 90946011f7 fix: update titleConvo to use same title protocal as node-chatgpt-api 2023-04-08 00:36:58 +08:00
Wentao Lyu 06b90f6a77 feat: add host params to bingAI.
[but seems not work in China]
2023-04-08 00:14:44 +08:00
Wentao Lyu 96b004a696 feat: add animation to New Topic. 2023-04-08 00:14:15 +08:00
Wentao Lyu 9623fe2e9f clean code with newConversationId in askbingai
Revert "Merge pull request #167 from danny-avila/fix-sydney"

This reverts commit 15999bda95, reversing
changes made to e1c6517b8f.
2023-04-07 23:13:13 +08:00
Danny AvilaandGitHub d79d297441 Merge pull request #172 from danny-avila/dano/fix-vite-memory-allocation-issue
Fix: fix javascript heap out of memory error from vite
2023-04-07 10:44:48 -04:00
Daniel D Orlando fd5fba45e6 remove unnecessary code 2023-04-07 07:36:28 -07:00
Daniel D Orlando 6e42d4fa3d Fix: fix javascript heap out of memory error from vite 2023-04-07 07:27:05 -07:00
Danny AvilaandGitHub 5ea8f75f70 Merge branch 'main' into dano/react-query-typescript 2023-04-07 10:19:13 -04:00
Danny AvilaandGitHub 7ec90a3585 Merge pull request #168 from danny-avila/feat-export-convo
fix: remove use-screenshot
2023-04-07 10:15:43 -04:00
git-bruh fc91ed49bc fix: replace various anchor tags with buttons to prevent text selection on repeated clicks 2023-04-07 19:25:30 +05:30
Danny AvilaandGitHub 6ce1b9d850 Update README.md 2023-04-07 09:42:36 -04:00
Daniel D Orlando c983670b9e Fix bug - when clicking on title in search results, was not switching to conversation 2023-04-07 06:02:28 -07:00
Daniel D Orlando e0f9e92bfc fix bad setState warning in console 2023-04-07 05:20:14 -07:00
Wentao Lyu ca720efde8 fix: remove use-screenshot
feat: displat file type
2023-04-07 13:21:20 +08:00
Danny AvilaandGitHub 34c3663308 Update README.md 2023-04-07 00:06:05 -04:00
Danny AvilaandGitHub 15999bda95 Merge pull request #167 from danny-avila/fix-sydney
fix: undo use of newConversationId which broke sydney
2023-04-06 20:26:29 -04:00
Daniel Avila 6a77401978 fix: undo commit 3b94a98 which broke sydney 2023-04-06 20:25:28 -04:00
Danny AvilaandGitHub 644ff160fc Merge branch 'main' into dano/react-query-typescript 2023-04-06 19:47:36 -04:00
Danny AvilaandGitHub e1c6517b8f Merge pull request #164 from danny-avila/workflow
ci(playwright.yml): add Playwright tests workflow
2023-04-06 19:43:36 -04:00
Daniel Avila 8a243e12fb chore(landing.spec.js): comment out title expectation and update landing title text 2023-04-06 19:39:19 -04:00
Daniel Avila 3f42db4956 chore(playwright.yml): move 'Start API server' job to after 'Install Playwright Browsers' job
fix(landing.spec.js): change page.goto URL to 'http://localhost:3080/'
2023-04-06 19:33:33 -04:00
Daniel Avila 323e951d7f ci(playwright.yml): add environment variables for secrets BINGAI_TOKEN, CHATGPT_TOKEN, MONGO_URI, and OPENAI_KEY 2023-04-06 19:26:25 -04:00
Danny AvilaandGitHub 11e4928582 Merge pull request #166 from danny-avila/fix-format
fix: formatting for user messages
2023-04-06 18:59:40 -04:00
Daniel Avila 24cb6d4013 fix: formatting for user messages 2023-04-06 18:58:56 -04:00
Daniel Avila 19183678a3 build(playwright.yml): add caching for API and Client dependencies
Add caching for API and Client dependencies to speed up the build process.
2023-04-06 18:52:13 -04:00
Daniel D Orlando dccd766d91 remove unused import 2023-04-06 15:50:15 -07:00
Daniel D Orlando d24abf6a2a Cleanup App.jsx 2023-04-06 15:42:39 -07:00
Daniel Avila 6b1b0f967d build(playwright.yml): add API and Client dependencies installation and server start before running tests
feat(playwright.yml): add global dependencies installation before running tests
2023-04-06 18:38:12 -04:00
Daniel Avila a56c8696d3 fix(playwright.config.js): correct baseURL typo from 'http:/localhost:3080' to 'http://localhost:3080' 2023-04-06 18:31:02 -04:00
Daniel D Orlando 7b7ba96786 Move createPayload and sse to data-provider, create TSubmission type 2023-04-06 14:56:33 -07:00
Daniel D Orlando 0fb9820110 change fetchById to call getConversationById 2023-04-06 14:56:33 -07:00
Daniel D Orlando c271f044c7 remove swr 2023-04-06 14:56:33 -07:00
Daniel D Orlando ec2ddc168b delete fetchers.js 2023-04-06 14:56:33 -07:00
Daniel D Orlando 0d5b51ec8c use create preset mutation for preset import 2023-04-06 14:56:33 -07:00
Daniel D Orlando 06a7ed31ac remove unused endpoints 2023-04-06 14:56:33 -07:00
Daniel D Orlando 4eff1c03dd package.lock 2023-04-06 14:56:33 -07:00
Daniel D Orlando 83df28f45d add RQ tokenizer 2023-04-06 14:56:33 -07:00
Daniel D Orlando 48e33fe1e9 Add support for deleting individual presets 2023-04-06 14:56:33 -07:00
Daniel D Orlando fbeff7a461 Code cleanup 2023-04-06 14:56:33 -07:00
Daniel D Orlando 61cb2858bb refactor and optimize search, add RQ for search 2023-04-06 14:56:33 -07:00
Daniel D Orlando 3d0bfaef51 Add presets and endpoints data services 2023-04-06 14:56:33 -07:00
Daniel D Orlando f2d18c81fc add deletePresetMutation to NewConverationMenu 2023-04-06 14:56:33 -07:00
Daniel D Orlando 68041d68ae fix types 2023-04-06 14:56:33 -07:00
Daniel D Orlando 93b685a1a2 change endpoints.ts to api-endpoints.ts 2023-04-06 14:56:33 -07:00
Daniel D Orlando 9e708225aa Add preset mutation 2023-04-06 14:56:33 -07:00
Daniel D Orlando 1cb8ef9803 feat: convert Chat.jsx to RQ 2023-04-06 14:56:00 -07:00
Daniel D Orlando 573112de7b fix: fix conversations in nav (put refreshConvoHint back) 2023-04-06 14:56:00 -07:00
Daniel D Orlando dd0a91a9f6 add RQ to clear all conversations 2023-04-06 14:56:00 -07:00
Daniel D Orlando 94e0636b32 add delete conversation mutation, fix withAuthentication on post requests 2023-04-06 14:56:00 -07:00
Daniel D Orlando bd53b878d4 update react-query-service 2023-04-06 14:56:00 -07:00
Daniel D Orlando c6d3bcd457 feat: Add RQ to Conversation component, create temp Chat component with RQ for compare and debugging 2023-04-06 14:56:00 -07:00
Daniel D Orlando 39f53e6ddf add QueryClientProvider to main 2023-04-06 14:56:00 -07:00
Daniel D Orlando 10941bf623 add DS_Store to gitignore 2023-04-06 14:56:00 -07:00
Daniel D Orlando 8c392ac05e build: add react query 2023-04-06 14:56:00 -07:00
Daniel D Orlando 9dae1ade60 turn off no-debugger in eslintrc for debugginer purposes 2023-04-06 14:55:26 -07:00
Daniel D Orlando ccc2f392e2 feat: add conversation query to nav 2023-04-06 14:55:26 -07:00
Daniel D Orlando 2048e34311 feat: add new data services to App.jsx 2023-04-06 14:55:26 -07:00
Daniel D Orlando 2589754171 feat: add data-provider 2023-04-06 14:54:37 -07:00
Daniel Avila 4510f04073 feat(playwright.yml): add GitHub Actions workflow for running Playwright tests on push and pull request events on main and master branches 2023-04-06 17:34:47 -04:00
Daniel Avila e98ce09d6b ci(playwright.yml): add Playwright tests workflow
fix(Landing.jsx): add id attribute to landing page title
test(landing.spec.js): update landing page title and h1 text content assertions
2023-04-06 17:33:02 -04:00
Danny AvilaandGitHub 21920dd864 Merge pull request #155 from danny-avila/feat-export-convo
Feature support export conversation
2023-04-06 17:11:59 -04:00
Daniel Avila 7d45d229af refactor(PresetItem.jsx): swap order of the edit and delete preset buttons 2023-04-06 17:07:44 -04:00
Daniel Avila 5dad9da918 chore(docker-compose.yml): instructions for CHATGPT_REVERSE_PROXY .env variable 2023-04-06 17:06:42 -04:00
Daniel Avila e0b0b68346 feat(Conversation.jsx): set document title when conversation is switched 2023-04-06 16:52:05 -04:00
Danny Avila 31cef16cc3 fix: fileName formatting fixes 2023-04-06 16:18:36 -04:00
Danny Avila 4245b43140 fix: used forked repo of use-react-screenshot for dep updates 2023-04-06 16:00:05 -04:00
Wentao Lyu 5664a0c2a5 fix: remove blank in screenshot 2023-04-07 02:00:51 +08:00
Danny AvilaandGitHub dde6de6bd5 Merge pull request #163 from danny-avila/fix-markdown
fix: markdown formatting errors
2023-04-06 13:55:04 -04:00
Danny Avila e077f2b73d refactor(askChatGPTBrowser.js): remove unused titleConvo import
style(Message.jsx, style.css): adjust margins and paddings to improve readability
2023-04-06 13:53:19 -04:00
Wentao Lyu 6e8a0a2f94 fix: didnt use preset from a exist convo 2023-04-07 01:49:28 +08:00
Wentao Lyu 96914387a6 feat: export to screenshot 2023-04-07 00:05:07 +08:00
Wentao Lyu 6f0b559927 feat: export conversation: csv, json, txt, markdown 2023-04-07 00:05:07 +08:00
Wentao Lyu 3b94a98719 fix: use new conversation Id 2023-04-07 00:05:07 +08:00
Daniel Avila 017447b064 chore(api): update chatgpt-api dependency to version 1.34.0
feat(api): use gen_title response for askChatGPTBrowser.js (official title)
2023-04-05 21:34:39 -04:00
Danny AvilaandGitHub 385eb2f398 Update README.md 2023-04-05 17:09:04 -04:00
Danny AvilaandGitHub 963939fe76 Update README.md 2023-04-05 17:02:00 -04:00
Danny AvilaandGitHub cf3902567e Update README.md 2023-04-05 16:58:54 -04:00
Danny AvilaandGitHub b4d451d7ae Merge pull request #142 from danny-avila/feat-endpoint-style-structure
Endpoint style structure and more customize of all model.
2023-04-05 15:07:27 -04:00
Danny Avila 97e39b8203 Merge branch 'main' into feat-endpoint-style-structure 2023-04-05 14:06:37 -04:00
Danny Avila 60f51dfeeb fix: remove messageHeader click when endpoint is browser 2023-04-05 14:05:54 -04:00
Wentao Lyu e7a6589958 fix: lost model of browser 2023-04-06 01:55:28 +08:00
Danny Avila d4cd9411c0 feat: add delete for presets in menu
[200~refactor(presets.js): remove unused getPreset function
refactor(presets.js): use arrow function syntax for map callback
refactor(presets.js): add console.log for debugging purposes
refactor(presets.js): simplify map callback syntax
refactor(presets.js): remove commented out code
refactor(FileUpload.jsx): remove commented out code
refactor(NewConversationMenu.jsx): rename data parameter to res for
clarity
refactor(NewConversationMenu.jsx): rename clearPresetsTrigger to
deletePresetsTrigger for clarity
refactor(NewConversationMenu.jsx): add onDeletePreset prop to
PresetItems component
refactor(PresetItem.jsx): add TrashIcon component and onDeletePreset
prop
refactor(PresetItems.jsx): add onDeletePreset prop to PresetItem
component
2023-04-05 13:21:29 -04:00
Danny Avila e1c731299c fix(ui): change SelectDropdown to SelectDropDown in multiple files 2023-04-05 12:00:52 -04:00
Danny Avila 214067cfcb feat(client): add doubleClickHandler prop to Slider component to reset to default
feat(client): add doubleClickHandler to temperature, topP, freqP, and presP sliders in OpenAI Settings component
2023-04-05 11:28:20 -04:00
Danny Avila 7d3df376ef style(Settings.jsx): add openDelay prop for faster HoverCard views 2023-04-05 10:29:06 -04:00
Wentao Lyu 474ced1dbe fix: typo in askBingAI.js 2023-04-05 22:13:38 +08:00
Danny Avila 55e949578f style(MessageHeader.jsx): add hover opacity to the background color of the message header 2023-04-05 10:03:52 -04:00
Danny Avila b730d631af feat(client): add rc-input-number package to dependencies
feat(client): add classnames package to dependencies
2023-04-05 09:41:46 -04:00
Wentao Lyu 03cade8bd5 fix: input style
fix: use ChatGptBrowser
2023-04-05 21:21:31 +08:00
Wentao Lyu 22b9524ad3 feat: update env example.
feat: support OPENAI_REVERSE_PROXY
feat: support set availModels in env file

fix: chatgpt Browser send logic refactor.

fix: title wrong usage of responseMessage
BREAKING: some env paramaters has been changed!
2023-04-05 21:21:02 +08:00
Wentao Lyu a5202f84cc fix: force reset to default if 0 or false 2023-04-05 17:25:35 +08:00
Wentao Lyu 4b373ebc0e feat: range number input in options. 2023-04-05 17:14:05 +08:00
Wentao Lyu 0bc2db08c7 style: options in mobile 2023-04-05 16:27:11 +08:00
Wentao Lyu a641a0e373 fix: dont clear the input message if conversationId changed. 2023-04-05 16:16:26 +08:00
Wentao Lyu 6abe34ee3b fix: validate the import preset 2023-04-05 16:16:11 +08:00
Wentao Lyu 8c2d577e60 feat: remove customGpts 2023-04-05 16:15:46 +08:00
Daniel Avila 5aa6b516ed feat: Import Presets from NewConversationMenu
feat(FileUpload.jsx): add support for importing JSON files and fetching presets after import
feat(NewConversationMenu.jsx): add FileUpload component to select and import JSON files
feat(fetchers.js): add handleFileSelected function to handle importing JSON files and uploading presets to the server
2023-04-04 23:18:58 -04:00
Daniel Avila 0846aa0436 docs(bingai.js): remove commented example response and add reference to demo file 2023-04-04 21:00:04 -04:00
Daniel Avila 3ab9b7f736 refactor(askOpenAI.js): rename gptResponse to response, fixes titling error 2023-04-04 18:17:23 -04:00
Wentao Lyu 56e02f4968 change Switch to simple mode to close button 2023-04-05 03:58:22 +08:00
Wentao Lyu 46fba87f9a feat: add confirm of clear all presets. 2023-04-05 03:50:06 +08:00
Wentao Lyu ee10e0e43e feat: use default preset to create new conversation. 2023-04-05 03:49:54 +08:00
Wentao Lyu 579b53de29 feat: style match 2023-04-05 03:07:46 +08:00
Wentao Lyu 9f1ded7f75 feat: add jailbreak option for bingai
fix some bugs.
2023-04-05 02:46:22 +08:00
Wentao Lyu efb440128a code clean. 2023-04-05 02:30:25 +08:00
Wentao Lyu 010d900c90 fix: use universal setToneStyle 2023-04-05 02:30:14 +08:00
Wentao Lyu 0c4b754fba fix: to set default value when change endpoint of preset 2023-04-05 02:29:43 +08:00
Wentao Lyu d864da6a21 fix: rewrite ask openAI and ask BingAI. now all code cleaned. 2023-04-05 02:29:11 +08:00
Danny AvilaandGitHub 0ec68bf5a8 Update defaultSystemMessage.md 2023-04-04 13:35:34 -04:00
Danny AvilaandGitHub 82b4401e49 Update defaultSystemMessage.md 2023-04-04 13:35:10 -04:00
Danny AvilaandGitHub 99d238b0de Update defaultSystemMessage.md 2023-04-04 13:30:24 -04:00
Danny AvilaandGitHub 617aa6f499 Update defaultSystemMessage.md 2023-04-04 13:30:04 -04:00
Danny Avila f922a1d102 Merge branch 'feat-endpoint-style-structure' of https://github.com/danny-avila/chatgpt-clone into feat-endpoint-style-structure 2023-04-04 12:53:59 -04:00
Danny Avila bb75b6df3b feat(bingai.js): add context and systemMessage parameters to askBing function
feat(conversationPreset.js): add context and systemMessage fields to conversation preset schema
feat(askBingAI.js): pass context and systemMessage parameters to ask function
feat(Settings.jsx): add toneStyle prop to BingAISettings component
feat(BingAIOptions/index.jsx): add useEffect to check if advanced mode is needed
feat(cleanupPreset.js): add context and systemMessage fields to cleaned up preset object
feat(getDefaultConversation.js): add context and systemMessage fields to default conversation object
feat(handleSubmit.js): add context and systemMessage fields to message object
2023-04-04 12:53:41 -04:00
Danny AvilaandGitHub 4b04959290 Update defaultSystemMessage.md 2023-04-04 11:22:23 -04:00
Danny AvilaandGitHub 28c5b21217 Update defaultSystemMessage.md 2023-04-04 11:21:30 -04:00
Daniel D Orlando 086267ee01 Add esbuild to package.json 2023-04-04 07:32:28 -07:00
Danny Avila 3484ff687d Merge branch 'feat-endpoint-style-structure' of https://github.com/danny-avila/chatgpt-clone into feat-endpoint-style-structure 2023-04-04 10:08:12 -04:00
Danny Avila 6e233accf5 style(tokenizer.js): comment out console.log statement
fix(Settings.jsx): handle null or empty context in useEffect to avoid errors
2023-04-04 10:07:48 -04:00
Danny Avila 1a196580b2 feat: count tokens for context
feat(BingAI): convert toneStyle to lowercase before setting it in state
feat(BingAI): pass setToneStyle function to Settings component
refactor(BingAI): remove unused import and change setOption to setToneStyle in Settings component
refactor(fetchers.js): add axiosPost function for debounced axios post requests
2023-04-04 10:04:21 -04:00
Wentao Lyu e1d52b858b fix: cannot open edit preset the use preset. 2023-04-04 22:01:01 +08:00
Wentao Lyu cef98070e9 style: show scrollbar over content. 2023-04-04 13:27:09 +08:00
Wentao Lyu 7353829719 refactor: code clean up 2023-04-04 13:11:59 +08:00
Daniel Avila 89e38d67f4 feat(bing-settings): Work in Progress, will finish tomorrow
feat(api): add @dqbd/tiktoken package as a dependency
feat(api): add /api/tokenizer endpoint to tokenize text using @dqbd/tiktoken
feat(client): add toneStyle dropdown to BingAI Settings component
feat(client): add token count to BingAI Settings component

style(ui): add z-index to Dropdown and EndpointOptionsPopover components

Add z-index to Dropdown and EndpointOptionsPopover components to ensure they are displayed above other components.
2023-04-03 21:18:19 -04:00
Danny AvilaandGitHub 825be5aa4d Update defaultSystemMessage.md 2023-04-03 20:12:56 -04:00
Danny AvilaandGitHub d8deb89e3a Update defaultSystemMessage.md 2023-04-03 20:07:24 -04:00
Danny AvilaandGitHub da69e4176a Create defaultSystemMessage.md 2023-04-03 20:06:55 -04:00
Danny AvilaandGitHub 85372118ed Delete defaultSystemMessage 2023-04-03 20:06:36 -04:00
Danny AvilaandGitHub 4fe48fe6bb Create defaultSystemMessage 2023-04-03 20:06:08 -04:00
Daniel Avila 03f63975cc Merge branch 'main' into feat-endpoint-style-structure 2023-04-03 17:30:59 -04:00
Danny Avila 28f1e851f9 chore(Settings.jsx): comment out unused import of InputNumber component 2023-04-03 16:29:02 -04:00
Danny Avila 58d162d8b4 reset package-lock files 2023-04-03 14:39:27 -04:00
Danny Avila 4961cc9250 reset package-lock on root dir 2023-04-03 14:24:32 -04:00
Wentao Lyu 8720f39def feat: preserve the title of preset. 2023-04-04 01:39:40 +08:00
Wentao Lyu 089d99e679 feat: open option dialog when click messageHeader 2023-04-04 01:23:37 +08:00
Wentao Lyu d2579b44d1 feat: support edit preset,
feat: support view current conversation options.
feat: save current conversation as a preset.
feat: preset delete all.
2023-04-04 01:12:14 +08:00
Wentao Lyu dae0c2d5e3 server fix: code refactor 2023-04-04 01:10:50 +08:00
Danny AvilaandGitHub 3d246df6c9 Merge pull request #149 from adamrb/main
Support local config for reverse proxy
2023-04-03 13:07:45 -04:00
Wentao Lyu ea88e9b981 fix: bingAI/settings: wrong usage of checkbox 2023-04-04 01:04:33 +08:00
Adam BoeglinandGitHub 584772b3f2 Support local config for reverse proxy 2023-04-03 09:29:52 -07:00
Wentao Lyu e7c9ae5a7d fix: regenerate should work for error message. 2023-04-03 21:32:21 +08:00
Wentao Lyu 4a95b30a0d fix: proxy auth in vite.config.js 2023-04-03 21:31:59 +08:00
Wentao Lyu 15ada95249 feat: export preset. 2023-04-03 12:54:15 +08:00
Daniel Avila ced65f8c98 feat(client): add @radix-ui/react-checkbox dependency and create BingAI Settings component
feat(BingAIOptions): add advanced mode to BingAIOptions component
feat(Checkbox): add Checkbox component to ui components
2023-04-02 18:45:41 -04:00
Daniel Avila 871bc4c78b refactor(getIcon.jsx): if model is GPT-4, change background color to black. 2023-04-02 15:23:28 -04:00
Daniel Avila 24a82c8018 Merge branch 'main' into feat-endpoint-style-structure 2023-04-02 15:10:21 -04:00
Danny AvilaandGitHub cd7e3f3774 Merge pull request #148 from danny-avila/update-proxy
fix(chatgpt-browser.js): update reverseProxyUrl to use new domain nam…
2023-04-02 14:54:47 -04:00
Daniel Avila ab7cf8c881 fix(chatgpt-browser.js): update reverseProxyUrl to use new domain name 'https://bypass.churchless.tech' instead of 'https://bypass.duti.tech' 2023-04-02 14:54:02 -04:00
Daniel Avila aa4fd57459 feat(chatgpt-browser): add support for multiple GPT models
This commit adds support for multiple GPT models in the chatGPT-browser
client. The available models are now stored in a Map object, which maps
the model label to its corresponding model.

The commit adds a new component, ChatGPTOptions, to the client
UI to allow the user to select the GPT model to use in the chat. The
component is only displayed when the chatGPT-browser endpoint is
selected.
2023-04-02 14:34:12 -04:00
Danny AvilaandGitHub 8551995a51 Merge pull request #138 from danny-avila/danorlando/setup-e2e-with-playwright
test: Playwright setup
2023-04-02 14:28:23 -04:00
Daniel D Orlando 85f3b488ce test: move playwright action out of workflows 2023-04-02 11:11:53 -07:00
Daniel D OrlandoandDaniel D Orlando 12b7e9a6bb reset back to npm ci 2023-04-02 11:07:48 -07:00
Daniel D OrlandoandDaniel D Orlando d5e062eeed try using npm install instead of npm ci in github action 2023-04-02 11:07:48 -07:00
Daniel D OrlandoandDaniel D Orlando f5e120c330 try explicitly setting registry before npm ci step 2023-04-02 11:07:48 -07:00
Daniel D OrlandoandDaniel D Orlando afaa0253b8 undo change adding .npmrc to try to resolve the package resolution issue on npm ci step of github action 2023-04-02 11:07:48 -07:00
Daniel D OrlandoandDaniel D Orlando 3ec2942365 try manually removing @playwright from the resolution path to make github action work 2023-04-02 11:07:48 -07:00
Daniel D OrlandoandDaniel D Orlando 6d7f0448ff try adding .npmrc to resolve problem getting playwright-core from the registry 2023-04-02 11:07:48 -07:00
Daniel D OrlandoandDaniel D Orlando 70427a628f generate new package-lock.json 2023-04-02 11:07:48 -07:00
Daniel D OrlandoandDaniel D Orlando b19ef425b7 try removing package-lock.json and forcing it to generate on the npm ci step 2023-04-02 11:07:48 -07:00
Daniel D OrlandoandDaniel D Orlando b7c911534c use node 18 2023-04-02 11:07:48 -07:00
Daniel D OrlandoandDaniel D Orlando fe6b1fc12a generate new package-lock.json 2023-04-02 11:07:48 -07:00
Daniel D OrlandoandDaniel D Orlando bd94c4233d Playwright setup 2023-04-02 11:07:48 -07:00
Daniel Avila eef2303c8e Merge branch 'main' into feat-endpoint-style-structure 2023-04-02 12:10:14 -04:00
Danny AvilaandGitHub 6f30b6ec9d Merge pull request #147 from danny-avila/lift-api
chore(api): update "@waylaidwanderer/chatgpt-api" dependency to versi…
2023-04-02 12:08:33 -04:00
Daniel Avila 709dbbbc4b chore(api): update "@waylaidwanderer/chatgpt-api" dependency to version 1.33.2 2023-04-02 12:07:59 -04:00
Daniel Avila 77b46cc1a7 refactor(ModelDropDown.jsx): remove commented out code and simplify model display 2023-04-02 11:37:10 -04:00
Danny AvilaandGitHub f069d6b621 Update README.md 2023-04-02 08:27:38 -04:00
Daniel Avila f187da3afa reset package-lock 2023-04-02 00:03:54 -04:00
Daniel Avila 0564e3ed93 Merge branch 'main' into feat-endpoint-style-structure 2023-04-02 00:03:13 -04:00
Danny AvilaandGitHub 6cad5d5b50 Merge pull request #145 from danny-avila/fix-docker
fix docker issues with vite
2023-04-02 00:01:33 -04:00
Daniel Avila 8bc4d5e3fd refactor(Dockerfile): change COPY command to copy client side code from /client/dist instead of /client/public
refactor(Dockerfile): change port number from 3080 to 80
refactor(Dockerfile): remove unnecessary EXPOSE command
refactor(package.json): remove devDependencies for Vite and React plugin
2023-04-02 00:00:49 -04:00
Daniel Avila b14726b2dd fix docker issues with vite 2023-04-01 23:59:16 -04:00
Daniel Avila 3121a3a6ba fix: add brackets to main property for dialogtemplate 2023-04-01 19:42:09 -04:00
Daniel Avila 8fa20d9356 Merge branch 'main' into feat-endpoint-style-structure 2023-04-01 19:41:36 -04:00
Daniel Avila 48b901fdfe Merge branch 'master' of https://github.com/danny-avila/chatgpt-clone into search 2023-04-01 19:13:54 -04:00
Danny AvilaandGitHub c65a3b69ff Merge pull request #143 from danny-avila/DanO/use-vite-instead-of-webpack
feat: Use VITE in place of Webpack
2023-04-01 19:03:13 -04:00
Wentao Lyu 2cce2e30ba fix: change endpoint should not skip when endpoint same 2023-04-02 04:48:33 +08:00
Wentao Lyu ad5fbc5fb1 merge Daniel Avila's commit
feat(ModelDropDown.jsx): add optional props to customize component appearance and behavior

feat(client): add ModelDropDown component to OpenAIOptions component
2023-04-02 04:40:13 +08:00
Daniel D Orlando eb2d9bac33 refactor: have build served from dist folder 2023-04-01 13:28:37 -07:00
Wentao Lyu 45e17da241 feat: add preset and edit preset. 2023-04-02 04:15:07 +08:00
Wentao Lyu 80ef5008dd feat: add preset in server 2023-04-02 04:14:42 +08:00
Daniel D Orlando 6498ab79a4 build: install and configure Vite, move index.html 2023-04-01 12:58:49 -07:00
Daniel D Orlando 78dabe55ae refactor: rename tailwind config 2023-04-01 12:57:39 -07:00
Daniel D Orlando 64fc2e84f2 refactor: rename and fix postcss.config 2023-04-01 12:57:17 -07:00
Daniel D Orlando ff757f27cf fix: fix files to adhere to standard conventions 2023-04-01 12:56:32 -07:00
Daniel Avila 2157eb8f30 feat(ModelDropDown.jsx): add optional props to customize component appearance and behavior
feat(client): add ModelDropDown component to OpenAIOptions component
2023-04-01 15:29:38 -04:00
Daniel Avila 7487b59de7 Merge branch 'feat-endpoint-style-structure' of https://github.com/danny-avila/chatgpt-clone into feat-endpoint-style-structure 2023-04-01 14:27:40 -04:00
Daniel Avila efe55d8842 style(ui): replace SaveIcon with Save from lucide-react in EndpointOptionsPopover component 2023-04-01 14:27:34 -04:00
Wentao Lyu d76efa7874 code cleanup 2023-04-02 00:57:27 +08:00
Daniel Avila c5805d710b Merge branch 'feat-endpoint-style-structure' of https://github.com/danny-avila/chatgpt-clone into feat-endpoint-style-structure 2023-04-01 12:49:42 -04:00
Daniel Avila f5ffa81455 complete: modeldropdown 2023-04-01 12:49:20 -04:00
Wentao Lyu 4d7fa26e6c feat: move EndpointOptionsPopover as a common component 2023-04-02 00:29:53 +08:00
Wentao Lyu 099210c10e feat: add default text.
fix: slider should set a number
2023-04-01 23:45:19 +08:00
Wentao Lyu 46e3ef4049 feat: endpoint setting mobile style.
feat: save and endpoint option works!
2023-04-01 23:27:44 +08:00
Wentao Lyu edaf7c3ad1 feat: advanced style for openAI endpoint 2023-04-01 14:33:26 +08:00
Wentao Lyu 60be4f12b7 feat: return endpoint config from server 2023-04-01 14:33:07 +08:00
Daniel Avila b687ab30ed style(OpenAIOptions): add dark mode support to Settings2 icon
feat(OpenAIOptions): pass isOpen prop to Settings component to toggle visibility
2023-03-31 21:51:06 -04:00
Daniel Avila 467e24c9ed style(Input/OpenAIOptions): comment out unused code and add gray color to settings icon 2023-03-31 21:48:27 -04:00
Daniel Avila 9098362377 style(Settings.jsx): remove unnecessary line break
style(Settings.jsx): add text-sm class to Label components
style(Settings.jsx): add flex, h-10, max-h-10, w-full, resize-none, px-3, py-2, focus:ring-0, focus:ring-offset-0, focus:ring-opacity-0, focus:outline-none classes to Input component
2023-03-31 21:45:29 -04:00
Daniel Avila 064dfb5336 fix(Settings.jsx): change min value of Slider component to -2 to allow negative values for presP variable 2023-03-31 21:11:43 -04:00
Daniel Avila 404566c1fb update more button and misc. styling 2023-03-31 18:38:58 -04:00
Daniel Avila 92dbdcaaa2 refactor(OptionHover.jsx): simplify types object by removing unnecessary nesting and description keys 2023-03-31 17:35:30 -04:00
Danny Avila 97e3ff6b6f reset package-lock 2023-03-31 16:19:48 -04:00
Danny Avila b5541903e4 merge latest 2023-03-31 16:09:45 -04:00
Danny Avila 580d82ffe8 basic settings done for openAI 2023-03-31 16:08:52 -04:00
Wentao Lyu 5cb59885ec fix : endpoint option should hide on exist conversation 2023-04-01 03:02:16 +08:00
Wentao Lyu ec47879edc clean: parentMassageId is no more needed in conversation. 2023-04-01 02:15:09 +08:00
Wentao Lyu b67af67433 feat: support copy to clipboard
feat: move regenerate to all messages
feat: move stop generate to replace submit button
feat: make options' position more clear
2023-04-01 02:12:15 +08:00
Danny Avila e8b2c2059d feat: select options 2023-03-31 13:12:06 -04:00
Wentao Lyu bb1f8d731b feat: select model
feat: force to be advanced mode
2023-04-01 00:38:05 +08:00
Wentao Lyu 059006382d feat: add animation 2023-04-01 00:09:49 +08:00
Wentao Lyu 462660d554 feat: prototype of endpoint setting 2023-03-31 23:16:00 +08:00
Danny Avila b703d3706b refactor(BingStyles.jsx): use cn utility function to concatenate class names in defaultSelected variable 2023-03-31 08:36:42 -04:00
Wentao Lyu 8b86fd0901 fix: jailbreak should be boolean 2023-03-31 16:36:24 +08:00
Wentao Lyu a8e44d0028 feat: view endpoint setting in message header. 2023-03-31 16:35:30 +08:00
Wentao Lyu babc4da7ab remove yarn.lock 2023-03-31 04:41:24 +08:00
Wentao Lyu 03f1a439d6 fix: suggestions seems not used. what's this? 2023-03-31 04:39:11 +08:00
Wentao Lyu 96639b82a8 feat: bingstyles now support endpoint 2023-03-31 04:33:26 +08:00
Wentao Lyu e8e3903b78 feat: feat: new endpoint-style endpoint select
fix: a wrong use of bingai params
2023-03-31 04:22:16 +08:00
Wentao Lyu adcc021c9e feat: feat: new endpoint-style submit 2023-03-31 03:22:57 +08:00
Wentao Lyu 089ca5f120 feat: new endpoint-style icon 2023-03-31 01:41:41 +08:00
Wentao Lyu 7e8b31cd09 fix: a typo in authenticatedOrRedirect 2023-03-31 00:26:43 +08:00
Wentao Lyu c53778e7c1 feat: new endpoint-style structure in client side
NOT FINISHED. model menu and icon and send message not work.
2023-03-31 00:20:45 +08:00
Wentao Lyu dd825dc6d4 feat: new endpoint-style structure in server side 2023-03-31 00:20:18 +08:00
Wentao Lyu 36c126e7a5 feat: show toneStyle in conversation title bar 2023-03-30 18:18:06 +08:00
Daniel Avila 9b04ffabca chore(client): update postcss-loader and postcss-preset-env packages to latest versions
chore(client): update webpack package to latest version
2023-03-29 20:17:17 -04:00
Danny Avila cf2bab31cf reset packages and uninstall unused dep. 2023-03-29 14:12:24 -04:00
Danny Avila d6fdf41011 fix: add text to global state 2023-03-29 11:25:27 -04:00
Danny AvilaandGitHub 79bb54db9c Merge pull request #140 from danny-avila/feat-refactor
Code refactoring
2023-03-29 11:00:04 -04:00
Danny Avila 4a94ee7af8 feat: allow default gpt api model before frontend customization 2023-03-29 10:17:24 -04:00
Danny Avila 39ff9c1bc2 fix: resize bing tabs 2023-03-29 09:02:49 -04:00
Danny Avila b9699feb3b fix: prevent scroll to top on initial messages 2023-03-29 08:49:39 -04:00
Danny Avila f93df2aea6 lift react markdown 2023-03-29 08:19:00 -04:00
Wentao Lyu 2c1871d5ba fix: update url rule in nginx 2023-03-29 16:11:43 +08:00
Wentao Lyu e796a19136 fix: remove related messages when deleting conversations. 2023-03-29 13:32:01 +08:00
Daniel Avila 0d7300be9b fix: chatgptBrowser handling and ask.js refactor 2023-03-28 22:28:43 -04:00
Daniel Avila 005d8fb178 edit titleConvo for consistent results 2023-03-28 19:50:37 -04:00
Daniel Avila f53b620df5 chore: add back clear convo dialog 2023-03-28 19:10:22 -04:00
Wentao Lyu e706f0ea9e typo 2023-03-29 06:44:18 +08:00
Wentao Lyu e663270072 fix: show message list. 2023-03-29 05:48:44 +08:00
Danny Avila 74924d2eea reset package-lock again 2023-03-28 16:29:42 -04:00
Danny Avila a04ea81143 Merge branch 'feat-refactor' of https://github.com/danny-avila/chatgpt-clone into feat-refactor 2023-03-28 15:35:40 -04:00
Wentao Lyu e818ee913d fix: add typescript package for react 2023-03-29 03:34:13 +08:00
Danny Avila 2a16a64612 fix: cursor appears on placeholder message 2023-03-28 15:32:22 -04:00
Danny Avila 95e9f05688 chore: reset package lock file 2023-03-28 15:15:10 -04:00
Wentao Lyu ee3f6e1d1d remove react-redux.
help needed, please generate a package-lock.json
2023-03-29 02:42:37 +08:00
Wentao Lyu b7af3595cf cleanup remove redux store 2023-03-29 02:38:24 +08:00
Wentao Lyu aa26eea8c5 fix: missing icon of search result
feat: use search result message as single list
2023-03-29 02:29:15 +08:00
Danny AvilaandGitHub c4be973b78 Update README.md 2023-03-28 14:12:08 -04:00
Wentao Lyu dc743df255 feat: update title generator prompt, to support better on language. 2023-03-29 01:50:57 +08:00
Wentao Lyu 319e4f0f95 fix: set to default model in searchPlaceholderConversation
fix: set max auth cookie to 7 days
2023-03-29 01:26:58 +08:00
Wentao Lyu f595cb2aa1 fix: three dot looks wide on android chrome. 2023-03-29 01:09:03 +08:00
Wentao Lyu 894aad9f0b sync with main 2023-03-29 00:20:57 +08:00
Wentao Lyu 5467d550e5 Merge remote-tracking branch 'origin/main' into feat-refactor-0.1.1 2023-03-29 00:19:46 +08:00
Wentao Lyu d0d0a3d23e feat: print nothing found when no search result.
fix: handle 404 of conversation fetch failed
2023-03-29 00:18:27 +08:00
Wentao Lyu 370dc2dd8a feat: support search-style-url
fix: url can be null in conversationId and query
fix: get conversation api should handle not found.
2023-03-29 00:08:02 +08:00
Danny AvilaandGitHub d9363b93c6 Create .github/FUNDING.yml 2023-03-28 11:48:47 -04:00
Danny AvilaandGitHub b1b904ce5a Merge pull request #139 from danny-avila/fix-bugs
Fix bugs
2023-03-28 11:45:05 -04:00
Danny Avila 4564b648f7 fix: failsafe to prevent all convos clearing from delete button 2023-03-28 11:42:36 -04:00
Danny Avila 0fbbe74479 revert bing to last working state 2023-03-28 11:38:56 -04:00
Wentao Lyu 8ea98cca5d refactor: bing button
THIS IS NOT FINISHED. DONT USE THIS
2023-03-28 23:00:29 +08:00
Wentao Lyu c7c30d8bb5 refactor: basic message and send message. as well as model
THIS IS NOT FINISHED. DONT USE THIS
2023-03-28 22:39:27 +08:00
Wentao Lyu de8f519742 refactor: update package-lock 2023-03-28 20:37:34 +08:00
Wentao Lyu af3d74b104 refactor: nav and search.
feat: use recoil to replace redux
feat: use react-native

THIS IS NOT FINISHED. DONT USE THIS
2023-03-28 20:36:21 +08:00
Danny AvilaandGitHub 2ad675196f Merge pull request #137 from danny-avila/fix-css
fix: set max height on customgpt prompt prefix
2023-03-28 08:11:43 -04:00
Danny Avila e434b3afea fix: set max height on customgpt prompt prefix 2023-03-28 08:10:22 -04:00
Wentao Lyu d8ccc5b870 fix: clearConvo will remove all messages 2023-03-28 01:19:44 +08:00
Wentao Lyu 7d43032a98 feat: return home page on any path
fix: clearConvo will remove all messages
2023-03-28 00:15:29 +08:00
Danny AvilaandGitHub 5b8483828b Merge pull request #136 from danny-avila/fix-css
fix: css matches official more closely with new markdown handling
2023-03-27 09:56:14 -04:00
Danny Avila 3c23f16b98 fix: css matches official more closely with new markdown handling 2023-03-27 09:55:24 -04:00
Danny AvilaandGitHub 7dc479e0a0 Merge pull request #135 from HyunggyuJang/follow-up/title-generation
updateConvo for title updating
2023-03-27 09:31:45 -04:00
Danny AvilaandGitHub ee9419cb0b Merge pull request #133 from HyunggyuJang/refactor/markdown
Use react markdown as default, cleanup dependencies
2023-03-27 09:31:32 -04:00
Danny AvilaandGitHub 1c26bbe43e Merge pull request #128 from HyunggyuJang/sydney/tone-adjustment
Make tone adjustment available after conversation started if sydney
2023-03-27 09:31:16 -04:00
Danny AvilaandGitHub 52d57f67aa Merge pull request #127 from HyunggyuJang/bing/fast-tone
Add fast tab for bing tone
2023-03-27 09:31:03 -04:00
Hyunggyu Jang 7486a56816 updateConvo for title updating 2023-03-27 12:53:40 +09:00
Hyunggyu Jang 40e23b013a Use react markdown as default, cleanup dependencies 2023-03-27 10:22:37 +09:00
Hyunggyu Jang c119c4044a Adjust color 2023-03-26 19:52:47 +09:00
Hyunggyu Jang 6f037309ad Fix for mobile view 2023-03-26 13:49:55 +09:00
Hyunggyu Jang af4110ff15 Do not allow to override bing AI tone in the middle of conversation 2023-03-26 13:25:29 +09:00
Hyunggyu Jang 55f04ffa60 Make tone adjustment available after conversation started if sydney 2023-03-26 13:12:48 +09:00
Hyunggyu Jang e5cf51b2d6 Add fast tab for bing tone 2023-03-26 13:10:36 +09:00
Danny AvilaandGitHub e8e512a451 Merge pull request #126 from danny-avila/minor-updates
chore: minor updates
2023-03-25 10:21:41 -04:00
Daniel Avila c2967eafa4 chore: update docker-compose for interchangeable env for search, uninstalled unused deps 2023-03-25 10:20:39 -04:00
Danny AvilaandGitHub 853b4dfd49 Merge pull request #125 from danny-avila/upgrade-node
Upgrade node
2023-03-25 10:02:17 -04:00
Danny AvilaandGitHub e49adaa314 Merge pull request #124 from danny-avila/bing-styles
Bing styles
2023-03-25 10:01:19 -04:00
Daniel Avila 394cdcd9f4 fix: remove 'class=' error 2023-03-25 09:55:33 -04:00
Daniel Avila c5561434c8 lift node-api library 2023-03-25 09:53:39 -04:00
Daniel Avila 26e7a715e0 feat: complete bing styles (bing is passed tone style) 2023-03-25 09:40:36 -04:00
Daniel Avila b07b74ba54 feat: complete bing styles (view) 2023-03-25 09:34:00 -04:00
Danny AvilaandGitHub 70152174b9 Merge pull request #123 from HyunggyuJang/fix/unknown-lang
Fix Unknown language bug
2023-03-25 08:39:10 -04:00
Hyunggyu Jang 8bd29f6d98 Fix Unknown language bug 2023-03-25 18:40:12 +09:00
Daniel Avila b8720eec3d feat: dark mode for style tabs 2023-03-24 19:02:42 -04:00
Daniel Avila ad0f2408c8 Merge branch 'main' into bing-styles 2023-03-24 18:50:56 -04:00
Danny AvilaandGitHub e2ad9accbe Merge pull request #122 from danny-avila/fix-search
Fix search
2023-03-24 18:45:03 -04:00
Daniel Avila b496174b4c fix: correctly escapes user content in search results 2023-03-24 18:44:12 -04:00
Daniel Avila 3295eb806c chore: expose mongodb on port 27018 2023-03-24 18:27:51 -04:00
Daniel Avila 34bef48e84 fix: correctly searches bing messages 2023-03-24 18:26:59 -04:00
Danny Avila a6c93ad681 dark mode in progress 2023-03-24 16:31:27 -04:00
Danny Avila 89ab74a913 feat: complete frontend/backend tone handling 2023-03-24 16:21:10 -04:00
Danny AvilaandGitHub a46ec62532 Update README.md 2023-03-24 16:19:42 -04:00
Danny AvilaandGitHub 22a967927f Update README.md 2023-03-24 16:19:16 -04:00
Danny AvilaandGitHub 0aa7581358 Add files via upload
in-depth local instructions thanks to @fuegovic
2023-03-24 16:17:04 -04:00
Danny AvilaandGitHub 513cd28528 Update README.md 2023-03-24 16:16:06 -04:00
Danny Avila 83b88bd759 feat: complete view for styles 2023-03-24 14:46:07 -04:00
Danny AvilaandGitHub 4f18a471b0 Merge pull request #121 from danny-avila/fix-clear-mobile
fix: clear convos on mobile now has top z-index
2023-03-24 08:20:30 -04:00
Danny AvilaandGitHub 737abd54ac Merge pull request #120 from HyunggyuJang/refactor/sydney
Refactor sydney & fix error
2023-03-24 08:19:06 -04:00
Hyunggyu Jang 1de51467ec Fix forgetfulness for bing/sydney 2023-03-24 12:48:20 +09:00
Hyunggyu Jang dcc0ab98e1 refactor: Remove verbose user message update for sydney 2023-03-24 12:27:46 +09:00
Daniel Avila 730256dcda fix: clear convos on mobile now has top z-index 2023-03-23 23:18:36 -04:00
Danny AvilaandGitHub 40e793477b Update README.md 2023-03-23 16:30:58 -04:00
Danny AvilaandGitHub b856db4772 Update .env.example
fix typo
2023-03-23 16:26:22 -04:00
Danny AvilaandGitHub cab1cbceab Merge pull request #118 from danny-avila/fix-auth
fix: auth env var must have no value, as well as assigned sample_user in route
2023-03-23 15:41:58 -04:00
Danny Avila b73be0dcfa fix: auth env var must have no value, as well as assigned username incase a falsy value is set 2023-03-23 15:37:25 -04:00
Danny AvilaandGitHub c6fb3018e7 Merge pull request #117 from danny-avila/search-final
Search final
2023-03-23 13:37:55 -04:00
Danny Avila 95cf27ee3e edit env example 2023-03-23 13:30:55 -04:00
Danny Avila 7afe09fa02 chore: clear timeouts 2023-03-23 13:16:07 -04:00
Danny Avila bff33c79b3 merge latest pr from main 2023-03-23 13:04:40 -04:00
Danny Avila f73936e5f4 Merge branch 'main' into search-final 2023-03-23 13:02:52 -04:00
Danny Avila 2b8d37c38f edit env example 2023-03-23 12:45:42 -04:00
Danny Avila ca3da2505a edit env example 2023-03-23 11:49:54 -04:00
Danny AvilaandGitHub d3046dca07 Merge pull request #115 from HyunggyuJang/sydney/branching
Enable branching (edit message) for Sydney
2023-03-23 11:38:44 -04:00
Danny AvilaandGitHub 25fd39c2b9 Update README.md
organize readme for readability
2023-03-23 11:35:23 -04:00
Daniel Avila ab8724b568 edit docker-compose for meilisearch 2023-03-22 22:35:25 -04:00
Hyunggyu Jang c240d14864 Enable branching (edit message) for Sydney 2023-03-23 11:27:14 +09:00
Daniel Avila 1d464fdcfa reduce noisy meili errors if not set 2023-03-22 21:23:01 -04:00
Daniel Avila aacc292522 Merge branch 'main' into search-final 2023-03-22 20:57:20 -04:00
Danny AvilaandGitHub 350a1bbae0 Update README.md 2023-03-22 20:48:47 -04:00
Danny AvilaandGitHub 90b74aff2e Merge pull request #104 from HyunggyuJang/refactor/docker
Refactor: merge docker setup file into one dockerfile & one docker-compose.yml
2023-03-22 20:13:10 -04:00
Daniel Avila 37f36ec44a chore: update meilisearch compose 2023-03-22 20:12:38 -04:00
Daniel Avila 71b7eaa3f5 chore: update env example and add browser client config 2023-03-22 20:09:52 -04:00
Daniel Avila 719413f87a feat: syncs across document deletions 2023-03-22 19:53:09 -04:00
Daniel Avila 97634865eb feat: syncs across document deletions 2023-03-22 19:52:38 -04:00
Daniel Avila 1dbfb0dab7 minor styling changes, cache queried messages on server 2023-03-22 18:26:29 -04:00
Daniel Avila 0a671849b5 feat: clearing convos requires confirmation 2023-03-22 17:51:51 -04:00
Daniel Avila 655e7ce6d6 chore: improve meili error handling 2023-03-22 17:15:32 -04:00
Danny Avila 68979015c1 markdown styling changes in progress 2023-03-22 16:31:57 -04:00
Danny Avila 8f58c95452 feat: main styling/ui/ux final changes 2023-03-22 16:06:11 -04:00
Danny Avila 67161c983f chore: meilisearch setup config 2023-03-22 10:23:55 -04:00
Danny Avila 73449d9ec6 feat: build tree by convoId 2023-03-22 10:23:36 -04:00
HyunggyuJangandGitHub f5d102b7bd Update docker-compose.yml 2023-03-22 23:20:03 +09:00
Hyunggyu Jang 40ed6fa9ec Provide nginx docker build recipe 2023-03-22 22:59:29 +09:00
Danny Avila 5164cf46ac chore: error handling for complete omission of env var 2023-03-22 09:38:38 -04:00
Daniel Avila 277685c218 search: updating search endpoint (wip) 2023-03-22 01:34:36 -04:00
Daniel Avila e25aa74d7b search: correctly register/export schema/models, also made IIFE 2023-03-22 01:33:49 -04:00
Daniel Avila 47a6cfcafd chore: error controller (wip) 2023-03-22 01:32:55 -04:00
Daniel Avila 83a96706b4 chore: add prettier 2023-03-22 01:32:30 -04:00
Daniel Avila 75be4d9722 search: helper fn for invalid convoId strings 2023-03-22 01:31:49 -04:00
Daniel Avila a5cf2f9148 search: correctly register/export schema/models for mongoMeili 2023-03-22 01:31:01 -04:00
Daniel Avila 8be19f9982 search: sync on offset between meili and mongo 2023-03-22 01:30:04 -04:00
Hyunggyu Jang 36f3d37ecc Remove nginx setting 2023-03-22 10:50:59 +09:00
Hyunggyu Jang c233cc0d5c Move Dockerfiles into one toplevel Dockerfile 2023-03-22 10:50:44 +09:00
Daniel Avila 194051e424 feat: api will plugin mongoMeili without a valid connection 2023-03-21 20:06:27 -04:00
Daniel Avila 94c0fbb525 feat: api will disable search if no meilisearch connection 2023-03-21 19:44:31 -04:00
Daniel Avila 97a6cd801b feat: simple api call to enable search 2023-03-21 19:31:57 -04:00
Danny AvilaandGitHub 1041146fcb Merge pull request #108 from danny-avila/upgrade-fix
fix: chatgpt now using latest
2023-03-21 17:41:05 -04:00
Danny Avila e531a17e0f fix: chatgpt now using latest 2023-03-21 16:32:32 -04:00
Danny AvilaandGitHub 30a7a80bfc Merge pull request #107 from danny-avila/chores
Chores
2023-03-21 14:27:00 -04:00
Danny Avila 67f8374c9e chore: eslint rules and blinker update in text handling 2023-03-21 13:41:31 -04:00
Danny Avila 0cc4aea204 chore: reorg. content files, add blinking cursor 2023-03-21 09:46:08 -04:00
Danny Avila 04796824d5 chore: init eslint 2023-03-21 08:48:35 -04:00
Danny AvilaandGitHub 9020239e1f Merge pull request #103 from HyunggyuJang/use-lock-file
Use npm ci rather than install to be consistent with lock file
2023-03-21 08:31:09 -04:00
Hyunggyu Jang 0a12b47760 Use npm ci rather than install to be consistent with lock file 2023-03-21 12:23:25 +09:00
Danny AvilaandGitHub 9358a4fdb5 Merge pull request #102 from danny-avila/bing-hotfix
bing hotfix, latest api, uses sydney
2023-03-20 17:16:45 -04:00
Daniel Avila 7d796f2c3e bing hotfix, latest api, uses sydney 2023-03-20 17:02:37 -04:00
Danny AvilaandGitHub 0a1651f6a1 Update README.md 2023-03-20 04:44:42 -04:00
Daniel Avila d13315c45b chore: comment out auth route 2023-03-20 02:58:41 -04:00
Daniel Avila 0b75d5d6fe chore: another hotfix patch on getConvoTitle 2023-03-20 02:30:08 -04:00
Wentao LyuandDaniel Avila 39819b744c doc: add magic of generate auth code 2023-03-20 02:22:04 -04:00
Daniel Avila 28c8f066d9 chore: add latest api as dep with alias 2023-03-20 02:18:03 -04:00
Daniel Avila c85602b93b chore: Replace hard coded message ID with unique one 2023-03-20 01:51:07 -04:00
Daniel Avila 08c91871c7 chore: hotfix: browser will not leave empty convo 2023-03-20 01:42:06 -04:00
Daniel Avila 80ca3bc375 chore: hotfix for browser client 2023-03-20 01:35:02 -04:00
HyunggyuJangandDaniel Avila 0405206438 Remove crypto from client side
Unless, if we tries to access the client side page from http protocol, crypto.randomUUID() isn't available.
2023-03-20 00:58:17 -04:00
Daniel Avila 0af8f6a699 chore: fix broken browser client 2023-03-20 00:51:56 -04:00
Daniel Avila b0936fa322 chore: unplug meilisearch, add leading option to throttle 2023-03-20 00:48:16 -04:00
Daniel Avila 4cd0ff2682 fix: throttle scroll to bottom 2023-03-19 11:45:03 -04:00
Daniel Avila 4ce60537ca search result styling changes 2023-03-19 11:25:12 -04:00
Daniel Avila 0b47218cd5 markdown library change 2023-03-19 01:14:19 -04:00
Daniel Avila d56aa2edef loads up to 20 messages, debugging markdown issue 2023-03-18 23:18:36 -04:00
Daniel Avila 4e6168d8fa setup message population on search 2023-03-18 18:40:53 -04:00
Daniel Avila 4197a92609 feat: search working as expected 2023-03-18 17:49:24 -04:00
Daniel Avila da42d6272a add loading state for fetching 2023-03-18 15:59:59 -04:00
Daniel Avila b97594c000 fix: conflicting fetch with /api/convos 2023-03-18 14:28:10 -04:00
Daniel Avila 0f54ffd8b4 feat: search bar working, still in progress 2023-03-18 01:40:49 -04:00
Daniel Avila 610cba4a60 backend logic drafted, moving to frontend 2023-03-17 22:20:36 -04:00
Daniel Avila 4f5ee8b198 move db functions 2023-03-17 19:58:13 -04:00
Daniel Avila 586c162404 merge commit 2023-03-17 19:40:44 -04:00
Danny AvilaandGitHub 1308ef1394 Merge pull request #88 from wtlyu/feat-clean-after-regenerate-pr
Feat clean after regenerate pr
2023-03-17 15:31:32 -04:00
Danny Avila 1513c27f7d adjust: wording and placeholder text size 2023-03-17 14:48:21 -04:00
Wentao Lyu 0ff3bbb28f clean up 2023-03-18 02:15:24 +08:00
Wentao Lyu 7987c0100c fix: add crypto as deps. [need confirm] 2023-03-18 02:04:51 +08:00
Wentao Lyu e8611a1d07 fix: don't reset isSubmitting in useEffect of submission 2023-03-18 01:54:06 +08:00
Wentao Lyu ce78123369 fix: allow delete of last char while submitting 2023-03-18 01:24:35 +08:00
Wentao Lyu a90db1f1a4 fix: add model to customGpts
fix: filter models by model only
2023-03-18 01:12:45 +08:00
Wentao Lyu a213868b17 fix: set isSubmitting with messages together
style: some notification
2023-03-18 01:12:45 +08:00
Wentao Lyu 6b2a2bb858 feat: save cancelled flag in message 2023-03-18 01:11:44 +08:00
Wentao Lyu fea3afa740 fix: missing import 2023-03-18 01:10:20 +08:00
Wentao Lyu 7372b37fe6 style: hide footer in mobile mode
style: avoid ui overflow in mobile mode
2023-03-18 01:09:59 +08:00
Danny AvilaandGitHub e11ce141d7 Merge pull request #86 from danny-avila/fix-scroll
fix: fix weird scrolling behavior on last message
2023-03-17 12:38:15 -04:00
Danny Avila 46fbd3b66a fix: fix weird scrolling behavior on last message 2023-03-17 12:34:54 -04:00
Danny AvilaandGitHub ce3f03267a Merge pull request #80 from wtlyu/feat-regenerate-and-cancel
Feat regenerate and cancel
2023-03-17 10:40:45 -04:00
Danny AvilaandWentao Lyu 9a2392e4d5 fix: mobile styling after regen buttons 2023-03-17 22:20:23 +08:00
Danny AvilaandGitHub 1eab4d240d Merge pull request #85 from danny-avila/bing-refusal
Bing refusal
2023-03-17 09:14:40 -04:00
Danny Avila 5568a60174 handle bing message refusal 2023-03-17 09:11:45 -04:00
Danny Avila ea4180f22a remove detect code (not always accurate) 2023-03-17 09:11:31 -04:00
Daniel Avila 6d2f3361d0 feat: search in progress 2023-03-16 21:20:40 -04:00
Daniel Avila 9995a159aa feat: reorganize api files, add mongoMeili 2023-03-16 19:38:16 -04:00
Daniel Avila 854f1c3572 feat: search, refactoring messages model 2023-03-16 17:20:26 -04:00
Danny Avila dcc13daf67 testing: mongo meilisearch 2023-03-16 16:22:08 -04:00
Wentao Lyu 2310bab348 revert: dont overwrite the mobile input panel color 2023-03-17 03:54:15 +08:00
Wentao Lyu d64edfdc7d feat: add chrome title color 2023-03-17 03:50:34 +08:00
Wentao Lyu a8c53f1f0d mobile style, for input panel and regenerate buttons 2023-03-17 03:50:34 +08:00
Wentao Lyu ef9f1ee1cf feat: cancellable api request 2023-03-17 03:50:34 +08:00
Wentao Lyu 66ad54168a feat: regenerate for bingai 2023-03-17 03:50:34 +08:00
Wentao Lyu 0891566d1e feat: add regenerate to all response message as official 2023-03-17 03:49:59 +08:00
Wentao Lyu e3b0cb7db7 feat: show model at the top of conversation messages. 2023-03-17 03:48:45 +08:00
Danny Avila c27554ed2e fix: allow customs in model filter 2023-03-16 15:05:23 -04:00
Danny AvilaandGitHub 87f793f1c4 Merge pull request #79 from danny-avila/improve-titles
fix: add deterministic titling
2023-03-16 15:03:04 -04:00
Danny Avila 4078c5283b fix: add deterministic titling 2023-03-16 15:02:40 -04:00
Danny AvilaandGitHub 5cac7e48f0 Merge pull request #74 from wtlyu/feat-model-based-on-key
feat: show model based on configured Keys
2023-03-16 15:00:23 -04:00
Danny AvilaandGitHub 7a08c77850 Update README.md 2023-03-16 14:11:24 -04:00
Danny AvilaandGitHub 1fe9e29187 Merge branch 'master' into feat-model-based-on-key 2023-03-16 13:39:41 -04:00
Danny AvilaandGitHub ba8692dbe4 Merge pull request #59 from wtlyu/feat-usersys
Feat usersys
2023-03-16 13:35:03 -04:00
Danny AvilaandGitHub d30b406c4c Merge pull request #4 from wtlyu/revisions
Revisions
2023-03-16 13:34:36 -04:00
Danny Avila 915cda70ef fix: user:user redundancies 2023-03-16 13:32:04 -04:00
Danny Avila df19595c5b fix: icon and mobile textarea styling 2023-03-16 13:30:10 -04:00
Danny Avila 867b3073d4 fix: icon and mobile textarea styling 2023-03-16 13:29:13 -04:00
Danny AvilaandGitHub e4e28dbbe2 Update README.md 2023-03-16 10:57:44 -04:00
Danny AvilaandGitHub cdbc0e21e7 Update README.md 2023-03-16 10:57:18 -04:00
Wentao Lyu b6f7f95709 feat: set default model once model list read. 2023-03-16 21:12:33 +08:00
Wentao Lyu 131af50034 feat: show model based on configured Keys 2023-03-16 14:44:14 +08:00
Wentao Lyu 23c050b54e style: mobile style of landing 2023-03-16 14:07:48 +08:00
Wentao Lyu 7442294c41 fix: mobile sizing style of icon 2023-03-16 14:05:12 +08:00
Wentao Lyu a47dbe6262 feat: use same icon style in TextChat 2023-03-16 13:45:46 +08:00
Wentao Lyu aabb19656e feat: combine customgpt to user 2023-03-16 13:30:20 +08:00
Wentao Lyu b0284b6974 sync updates and merge with feat-resubmit 2023-03-16 13:20:54 +08:00
Wentao Lyu 62d88380e0 feat: add sample multi-user support
feat: update README
2023-03-16 13:20:34 +08:00
Daniel Avila 41f351786f fix: oversight, artifact in bing route 2023-03-15 22:36:16 -04:00
Danny AvilaandGitHub ffcfb69dee Merge pull request #72 from danny-avila/fix-mobile-switch
fix: mobile view for sibling switch
2023-03-15 19:06:41 -04:00
Daniel Avila c91ce36227 fix: mobile view for sibling switch 2023-03-15 19:05:17 -04:00
Danny AvilaandGitHub d06e58f043 Merge pull request #55 from wtlyu/feat-resubmit
Feature: multipath message & resubmit, conversation placeholder, separate title generation, and some bug fix.
2023-03-15 18:06:35 -04:00
Daniel Avila d052d221dc chore: switch focus to textarea when custom model change (still need to figure out reg model change) 2023-03-15 18:05:34 -04:00
Danny AvilaandGitHub ff45511011 Merge pull request #3 from wtlyu/final-adj
Final adjustments
2023-03-15 17:08:18 -04:00
Danny Avila 8c6340aed0 chore: refactor cursor blink, debugging 2023-03-15 16:38:01 -04:00
Danny Avila 84b104e65f chore: delegate response text parsing to one location 2023-03-15 15:44:48 -04:00
Danny Avila a0c94715ce chore: refactor titleConvo 2023-03-15 15:21:04 -04:00
Danny Avila a8aad30fc8 chore: memoized Messages component, will require custom equality check 2023-03-15 14:36:17 -04:00
Danny Avila 2fd50c99b8 fix: debounce title request and handle error with default title 2023-03-15 12:47:30 -04:00
Danny Avila 96ca783517 chore: re-organize message modules, fix icon size, convo reset properly rebuilds Tree 2023-03-15 10:42:45 -04:00
Wentao Lyu 45ca0a8713 fix: gptCustom icon should show as same in model and message 2023-03-15 14:42:39 +08:00
Wentao Lyu 5d0b849930 feat: show icon within model select menu
fix: use icon for gptCustom
2023-03-15 14:21:08 +08:00
Wentao Lyu 54aa9debb4 fix: don't reset new convo if model not change
fix: change model will clear all messages.
2023-03-15 13:38:01 +08:00
Daniel Avila 4e91437049 fix: convo resets, sets new Convo 2023-03-14 21:32:25 -04:00
Daniel Avila 918f2fecb6 fix: convo resets on model change 2023-03-14 21:25:02 -04:00
Daniel Avila 796d8031e8 fix: ensure custom params are not passed to non custom models 2023-03-14 20:21:41 -04:00
Daniel Avila 6e32f71565 fix: adjust custom client for new progress CB 2023-03-14 20:15:06 -04:00
Daniel Avila 6192c2964e fix: validation to avoid saving customGpt params to non-custom models 2023-03-14 20:14:38 -04:00
Daniel Avila 626a8fbd8e fix: if db is empty, will never show new convos until refresh 2023-03-14 18:53:46 -04:00
Danny AvilaandGitHub a8344ec5bf Merge pull request #2 from wtlyu/refactors
Refactors
2023-03-14 18:16:44 -04:00
Danny AvilaandGitHub c230fe41f4 Merge branch 'feat-resubmit' into refactors 2023-03-14 16:09:25 -04:00
Danny Avila d0ef0f84c8 chore: delegate text handling to one place, html sanitization in progress 2023-03-14 16:05:46 -04:00
Wentao Lyu 8289558d94 feat: pagination in nav 2023-03-15 04:05:14 +08:00
Danny Avila 2e20b28c4d chore: refactor progressCB to one place, fix sydney, and sanitize html 2023-03-14 15:42:59 -04:00
Danny Avila 9a17e94f8f fix: refactor migration and sort old convos correctly 2023-03-14 14:51:26 -04:00
Wentao Lyu 71fc86b9a6 fix: buildTree should store parent-not-exist message as root. rather than dropping them. 2023-03-15 02:43:21 +08:00
Wentao Lyu 8882432210 fix: hide the edit button when bingai 2023-03-15 02:33:08 +08:00
Wentao Lyu 644f3f716f feat: re-orginazed three ask api. To provide ability to reproduce message.
feat: bing and sydney come to work again, [need more test]
2023-03-15 02:15:46 +08:00
Wentao Lyu 8b00805d24 fix: dont send gen_title twice 2023-03-15 02:15:46 +08:00
Wentao Lyu d73375958b feat: return error as a error message, not only text 2023-03-15 02:15:46 +08:00
Wentao Lyu 7168498543 fix: jailbreakConversationId=false response will be saved jailbreakConversationId='false' in database. 2023-03-15 02:15:46 +08:00
Wentao Lyu 27515cb00a fix: generate title by backend 2023-03-15 02:15:46 +08:00
Daniel AvilaandWentao Lyu 3e7ce67609 fix: migrate old schema to new 2023-03-15 02:15:46 +08:00
Daniel AvilaandWentao Lyu 4fd05e15b4 fix: migrate old schema to new 2023-03-15 02:15:46 +08:00
Wentao Lyu 0fa19bb6ad feat: save error message into database. 2023-03-15 02:15:46 +08:00
Wentao Lyu d9e5464b3b fix: cleanup debug msg 2023-03-15 02:15:46 +08:00
Wentao Lyu 953c5fc970 fix: w<1024px, will overflow. 2023-03-15 02:15:46 +08:00
Wentao Lyu 2afbc5883f fix: use onCompositionStart and onCompositionEnd to aviod enter submit when using input method. 2023-03-15 02:15:46 +08:00
Wentao Lyu 953f846958 fix: loading and send button, mobile style
feat: sibling switch, mobile style
fix: only the real submitting message will blink
feat: drop the text version username, use a similar square. (or it will mass up the sibling switch)
2023-03-15 02:15:46 +08:00
Wentao Lyu a4d5f6a3f2 feat: fully multipath and resubmit 2023-03-15 02:15:46 +08:00
Wentao Lyu 4a39965b22 fix: add proxy to titleConvo 2023-03-15 02:15:46 +08:00
Wentao Lyu 90dc171b34 test: generate of title shouldn't be mislead by answer 2023-03-15 02:15:46 +08:00
Wentao Lyu 0e98cb4206 fix: in mobile view, resubmit edit button should always visible 2023-03-15 02:15:46 +08:00
Wentao Lyu 9f8e9cb091 feat: gen title by sperate api call
feat:

fix: rename of convo should based on real request.
2023-03-15 02:15:46 +08:00
Wentao Lyu 8773878be2 feat: create conversation at the beginning then return the userMessage 2023-03-15 02:15:46 +08:00
Wentao Lyu 5a409ccfa6 fix: don't resubmit html label
fix: hide the resubmit editor border
2023-03-15 02:15:46 +08:00
Wentao Lyu 0ed8a40a41 feat: merge all message.id into message.messageId
feat: the first message will have a parentMessageId as 00000000-0000-0000-0000-000000000000 (in order not to create new convo when resubmit)
feat: ask will return the userMessage as well, to send back the messageId

TODO: comment out the title generation.
TODO: bing version need to be test

fix: never use the same messageId
fix: never delete exist messages
fix: connect response.parentMessageId to the userMessage.messageId
fix: set default convo title as new Chat
2023-03-15 02:15:46 +08:00
Wentao Lyu be71140dd4 fix: new message should append to the exist one 2023-03-15 02:15:46 +08:00
Wentao Lyu 6d51ec3e37 feat: auto hide edit button when edit is enabled. 2023-03-15 02:15:46 +08:00
Wentao Lyu bdfc895800 feat: support resubmit.
TODO: basic implementation. should add multi-path record in future.

feat: add deleteMessahesSince
feat: saveMessage will do createOrSave
feat: reorginazed submission
2023-03-15 02:15:46 +08:00
Wentao Lyu b9975ac283 fix: missing setSubmission 2023-03-15 02:15:46 +08:00
Wentao Lyu dd1f74da72 replace all created to timestamps: true in db 2023-03-15 02:15:46 +08:00
Danny AvilaandGitHub 5d65427a6e Merge pull request #56 from HyunggyuJang/build/docker-from-rootdir
Build docker files from root dir
2023-03-13 13:17:23 -04:00
Danny AvilaandGitHub 6a16d31f26 Merge pull request #60 from wtlyu/feat-autofocus
feat: auto focus
2023-03-13 13:15:43 -04:00
Wentao Lyu 8affbfdb4a feat: auto focus 2023-03-14 01:12:11 +08:00
Hyunggyu Jang 70d59c5b3c Build docker files from root dir 2023-03-13 22:49:40 +09:00
Danny AvilaandGitHub 73979ee67f Merge pull request #54 from danny-avila/minor-fixes
fix: model menu key issue
2023-03-12 16:46:25 -04:00
Daniel Avila 8e513d83a5 fix: model menu key issue 2023-03-12 16:45:44 -04:00
Danny AvilaandGitHub 9bc85ea83d Merge pull request #47 from wtlyu/feat-title-with-language
fix: give a stronger prompt to generate tile using the input language
2023-03-12 15:10:45 -04:00
Wentao Lyu 943eb5c74d fix: give a stronger prompt to generate tile using the input language 2023-03-13 01:51:32 +08:00
Danny AvilaandGitHub bac37cfe36 Update README.md 2023-03-11 23:29:27 -05:00
Danny AvilaandGitHub b2334054da Update README.md 2023-03-11 23:28:56 -05:00
Danny AvilaandGitHub a705e907ff Merge pull request #45 from danny-avila/submit-state
fixes: allow simultaneous convos & minor improvements
2023-03-11 23:28:33 -05:00
Daniel Avila c8bb6b13bf update: readme.md 2 2023-03-11 23:24:53 -05:00
Daniel Avila 50a17bc1b8 update: readme.md 2023-03-11 23:22:51 -05:00
Daniel Avila 3038015a5b fix: removes newline from start of stream response if present 2023-03-11 23:11:55 -05:00
Daniel Avila 20da895ee7 update bypass proxy url for browser client 2023-03-11 23:08:04 -05:00
Daniel Avila 8762765dd0 fix: retain scroll view on delete convo 2023-03-11 22:59:32 -05:00
Daniel Avila f1aabfa543 fix: reset submission when convo clears 2023-03-11 22:35:39 -05:00
Daniel Avila 23de688bf3 fix: stops stream upon conversation 2023-03-11 21:42:08 -05:00
Daniel Avila 79f050bac7 feat: loading state for messages 2023-03-11 18:39:46 -05:00
Danny AvilaandGitHub afae13eec6 Merge pull request #43 from danny-avila/fix-custom-name
fix: format sender text change and mobile styling
2023-03-11 17:38:17 -05:00
Daniel Avila 4fa599e97a fix: format sender text change and mobile styling 2023-03-11 17:36:10 -05:00
Danny AvilaandGitHub 3b1d6aaa79 Merge pull request #42 from danny-avila/title-lang
feat: title the chat in user's language
2023-03-11 16:28:36 -05:00
Daniel Avila 339230cd74 feat: title the chat in user's language 2023-03-11 16:26:41 -05:00
Daniel Avila 95bad60f7d fix: detectCode bug 2023-03-11 14:46:21 -05:00
Danny AvilaandGitHub 950b8f3f1e Merge pull request #34 from wtlyu/feat-mobile
feat: basic support mobile mode.
2023-03-11 12:59:09 -05:00
Danny AvilaandGitHub 0e2e5b8393 Update README.md 2023-03-11 12:32:59 -05:00
Danny AvilaandGitHub d2cb9957fe Merge pull request #35 from danny-avila/rm-short-filter
fix: rm short filter
2023-03-11 12:10:56 -05:00
Daniel Avila 78b4004ead fix: rm short filter 2023-03-11 12:10:00 -05:00
Danny AvilaandGitHub 8624062488 Merge pull request #26 from wtlyu/master
support config host name and proxy address. and fix a docker bug
2023-03-11 12:05:16 -05:00
Wentao Lyu 070fee2ece feat: basic support in mobile mode. including:
navbar show and hide,
similar fade animation,
auto close when select new convo,
mobile title will change with convo,
new chat button.
2023-03-12 00:32:03 +08:00
Danny AvilaandGitHub b1569450fd Merge pull request #32 from danny-avila/fix-hjs
Fix hjs
2023-03-11 10:32:38 -05:00
Daniel Avila 09659be002 fix: unsupported lang handler in client 2023-03-11 10:28:46 -05:00
Wentao Lyu 5e4fa09dcb update readme, add proxy and host information 2023-03-11 16:14:24 +08:00
Wentao Lyu 16c9589058 fix: use quotes outside of key=value in docker-compose.yml
fix: set HOST=0.0.0.0 by default in api's dockerfile
2023-03-11 16:14:11 +08:00
Wentao Lyu 8604030404 Add more information in env.example.
Give current host address when HOST=0.0.0.0
2023-03-11 15:03:18 +08:00
Wentao Lyu 97668217b9 fix: add proxy to bingai too 2023-03-11 15:02:26 +08:00
Wentao Lyu c5c865a25f fix: frontend api request shouldn't hard code the domain and port. 2023-03-11 14:13:39 +08:00
Wentao Lyu 06c99154ac feat: support config host name and proxy address 2023-03-11 14:12:51 +08:00
Daniel Avila 9d71b58345 fix: handles unsupported hjs languages in client response 2023-03-10 22:21:15 -05:00
Danny AvilaandGitHub fb9f77ae5e Merge pull request #23 from danny-avila/tab-links
Add back Tab links
2023-03-10 13:18:54 -05:00
Danny Avila fc2b9bf7f2 feat: tab link component, edit wrapper 2023-03-10 13:12:24 -05:00
Danny Avila cdbe00ec6f feat: tab link component 2023-03-10 13:11:17 -05:00
Danny Avila 72ff47e204 Rolled back to v0.0.2 2023-03-10 12:55:45 -05:00
Danny AvilaandGitHub 57d3025717 Merge pull request #20 from danny-avila/override-links
feat: links open in new tab
2023-03-10 09:45:52 -05:00
Danny Avila 6c02558f1b feat: links open in new tab 2023-03-10 09:44:40 -05:00
Danny AvilaandGitHub 0c62863e52 Update README.md 2023-03-10 08:38:45 -05:00
Danny Avila d825721dad fix: update readme and compose file 2023-03-10 08:37:15 -05:00
Danny AvilaandGitHub 0b3b6f91fc Merge pull request #19 from wtlyu/master
support config host name and proxy address.
2023-03-10 08:21:22 -05:00
Wentao Lyu 3a9b532248 fix: frontend api request shouldn't hard code the domain and port. 2023-03-10 21:12:16 +08:00
Wentao Lyu 67156f4a7a feat: support config host name and proxy address 2023-03-10 21:12:16 +08:00
Daniel Avila 4e616cd2ed fix: error handling titles, remove openAI req 2023-03-10 07:43:43 -05:00
Danny AvilaandGitHub 12cf3405e4 Merge pull request #16 from danny-avila/updateAI
feat: add sydney (jailbroken bing) and more bing styling with citations
2023-03-09 20:34:28 -05:00
Daniel Avila 4383894ad3 finish update 2023-03-09 20:31:56 -05:00
Daniel Avila d0ecaabfd8 feat: cites text with links 2023-03-09 20:29:44 -05:00
Daniel Avila a286f027c8 model menu won't close on model deletion 2023-03-09 19:02:59 -05:00
Daniel Avila 0ba92c4c03 add new reducer to ensure conversations are renewed on model change 2023-03-09 18:57:37 -05:00
Daniel Avila 30936573ac feat: includes sources 2023-03-09 18:42:36 -05:00
Daniel Avila a451574760 feat: cites text with links 2023-03-09 18:07:36 -05:00
Danny Avila 5e57deab5f bing styling in progress 2023-03-09 16:09:53 -05:00
Daniel Avila eae82edb83 merge readme update 2023-03-08 22:33:25 -05:00
Daniel Avila 1e0cbbf1cc merge readme update 2023-03-08 22:32:51 -05:00
Daniel Avila 3986b4ec9e Merge branch 'master' of https://github.com/danny-avila/chatgpt-clone into updateAI 2023-03-08 22:31:40 -05:00
Daniel Avila 67054e7504 working sydney, need to test the other models 2023-03-08 22:30:29 -05:00
Daniel Avila 2c1ae68dc4 adding sydney in progress. only uses jailbreakConvoId and parentMsgId 2023-03-08 21:06:58 -05:00
Daniel Avila 69b3edc52c feat: sydney is functional 2023-03-08 19:47:23 -05:00
Danny AvilaandGitHub 732c75d145 Update README.md 2023-03-08 10:26:12 -05:00
Danny AvilaandGitHub 0f54251459 Merge pull request #12 from HyunggyuJang/fix/webpack-artifacts
webpack artifacts should exist for nginx image
2023-03-07 21:07:13 -05:00
Hyunggyu Jang 72ef76b8a0 webpack artifacts should exist for nginx image 2023-03-08 10:38:22 +09:00
Danny AvilaandGitHub 81abbdb335 Merge pull request #11 from HyunggyuJang/fix/docker-name
DockerFile is invalid name
2023-03-07 20:11:44 -05:00
Hyunggyu Jang 67ca79dd3f DockerFile is invalid name 2023-03-08 09:51:04 +09:00
Danny AvilaandGitHub 5af03bb3d8 Update README.md 2023-03-07 14:54:44 -05:00
Danny AvilaandGitHub e5d25f45e3 Merge pull request #7 from danny-avila/easy-config-setup
dockerize app, improve ui/ux, add setup instructions
2023-03-07 14:53:57 -05:00
Danny Avila ad967beaf8 update readme 2023-03-07 14:51:41 -05:00
Danny Avila a8eaf2452a update readme, setup files, and clear convo bug 2023-03-07 14:43:55 -05:00
Danny Avila 5ad32e211f update docker-compose 2023-03-07 14:23:56 -05:00
Danny Avila da61a2841f fix: customGpt persistence blindspot, also remove free gpt 2023-03-07 14:22:33 -05:00
Danny Avila da55ac3774 update readme 2023-03-07 13:58:57 -05:00
Danny Avila f2b092545f fix: convos correctly delete 2023-03-07 13:53:23 -05:00
Danny Avila 17c01aa252 fix: ignoreDir pointing correctly 2023-03-07 13:49:15 -05:00
Danny Avila 6be037323e chore: model menu functions behave as expected 2023-03-07 13:19:56 -05:00
Daniel Avila 191118b90b add buttons to custom model menu items 2023-03-06 21:43:49 -05:00
Daniel Avila 6d757ff91f improve custom model selection ux 2023-03-06 21:12:36 -05:00
Daniel Avila d93282640c modify starting files and readme 2023-03-06 18:17:43 -05:00
Daniel Avila b9e9203a75 Merge branch 'easy-config-setup' of https://github.com/danny-avila/chatgpt-clone into easy-config-setup 2023-03-06 16:59:03 -05:00
Danny Avila ee8eba9ad5 updating readme 2023-03-06 16:30:16 -05:00
Danny Avila 102327313e modify docker setup 2023-03-06 15:57:01 -05:00
Danny Avila 08f59819cf modify docker setup 2023-03-06 15:56:25 -05:00
Danny Avila d9930dd2bc edit docker setup 2023-03-06 14:38:34 -05:00
Danny Avila 453301db0e chore: edit readme and docker-compose 2023-03-06 14:30:58 -05:00
Danny Avila 52e529dcbe complete docker setup 2023-03-06 14:04:06 -05:00
Danny Avila ff7b016190 initial docker detup 2023-03-06 12:49:22 -05:00
Danny Avila f5e079742a chore: reorganize client files for docker 2023-03-06 10:47:06 -05:00
Danny Avila affbaaf1a5 chore: server setup complete 2023-03-06 10:28:37 -05:00
Danny Avila fca546af63 reorganize dirs for dockerize 2023-03-06 10:15:07 -05:00
Danny AvilaandGitHub 062cd1d838 Merge pull request #6 from danny-avila/easy-config-setup
chore: misc. fixes/improvements before containerizing
2023-03-06 09:27:14 -05:00
Danny Avila 6ae154cc42 optimize convo paginations 2023-03-06 08:58:52 -05:00
Danny Avila c617b15bcd disable prompts until complete 2023-03-06 08:50:36 -05:00
Danny Avila b750bd2970 update roadmap 2023-03-06 08:41:09 -05:00
Daniel Avila 8d60cbdc7a feat: dynamic custom model saving/updating in dialog 2023-03-05 17:02:00 -05:00
Daniel Avila 15986c078b update readme with 2nd use case img 2023-03-05 16:23:35 -05:00
Daniel Avila 15f3972b9f remove models caching 2023-03-05 15:30:40 -05:00
Danny AvilaandGitHub 3224dca3b8 Merge pull request #5 from danny-avila/convo-pagination
feat: convo pagination in increments of 12
2023-03-05 14:42:43 -05:00
Daniel Avila a98367d27f convo fetching in increments of 12 2023-03-05 14:41:50 -05:00
Danny AvilaandGitHub 80319c7fcc Merge pull request #4 from danny-avila/dark-mode-match
feat: match more official stylings
2023-03-05 13:32:59 -05:00
Daniel Avila dae7f8d822 match more official stylings 2023-03-05 13:31:12 -05:00
Danny AvilaandGitHub c8d8484d86 Merge pull request #2 from danny-avila/customChatGPT
feature: customize chat gpt
2023-03-04 21:47:48 -05:00
Daniel Avila f40f80f030 update readme 2023-03-04 21:46:58 -05:00
Daniel Avila fc899da944 convo change also changes model selection 2023-03-04 21:10:45 -05:00
Daniel Avila d64f914552 complete customGpt 2023-03-04 20:48:59 -05:00
Daniel Avila b4b0c123ba prompt prefix and label state changes when customGpt selected 2023-03-04 18:00:12 -05:00
Daniel Avila 9c3a78f96b customGpts persist through localStorage 2023-03-04 17:39:06 -05:00
Daniel Avila 62bb6ea8f8 refactor model menu items into component 2023-03-03 21:33:09 -05:00
Daniel Avila 0bec306e59 remove punctuation from titles 2023-03-03 21:31:37 -05:00
Daniel Avila 42c74f254c reduce stylesheet clutter 2023-03-03 21:31:05 -05:00
Danny Avila 9a56130750 save custom settings in progress 2023-03-03 16:33:02 -05:00
Danny Avila 214228542a complete customChatGpt model selection 2023-03-03 15:52:06 -05:00
Danny AvilaandGitHub 0f9cfd0395 Merge pull request #1 from danny-avila/copyCode
complete copyCode for code blocks
2023-03-03 09:46:49 -05:00
Danny Avila 92648554ad complete copyCode for code blocks 2023-03-03 09:45:20 -05:00
Danny Avila ac2229fbb6 add more detail to titleConvo prompt 2023-03-03 09:07:29 -05:00
Danny Avila ec7aaf01a4 add hoverbuttons 2023-03-03 08:51:33 -05:00
Danny Avila 50ff96cbc5 copy code in progress 2023-03-02 16:31:00 -05:00
Danny Avila 0d8755a03a update readme 2023-03-02 16:08:26 -05:00
Danny Avila d235bb7b1b complete language detection for code blocks 2023-03-02 16:07:36 -05:00
Danny Avila c4d0787b49 chatgpt now titles conversations 2023-03-02 13:52:41 -05:00
Daniel Avila 897c384ac9 fix code parsing bug 2023-03-01 21:59:13 -05:00
Daniel Avila cbcc1a1bb4 readme update 2023-03-01 21:41:24 -05:00
Daniel Avila 1f270d349a add official api, remove davinci, organize root dir 2023-03-01 21:39:57 -05:00
Danny AvilaandGitHub 90ac43c8c5 Create LICENSE.md 2023-02-28 17:21:29 -05:00
Daniel Avila 1bba86f2e2 minor changes 2023-02-26 14:10:44 -05:00
Daniel Avila 8e5148b390 fix embed wrapper not invoking immediately 2023-02-25 10:33:26 -05:00
Daniel Avila 0ee31f0443 fix codeWrapper issue, will produce funky text with ` commenting 2023-02-25 10:16:21 -05:00
Daniel Avila ed699d2c06 wrap code in progress, revert old message handling 2023-02-25 09:06:26 -05:00
Daniel Avila 0f6fcd5928 organize modules 2023-02-25 09:04:32 -05:00
Daniel Avila 78c9f3dc74 trying different markdown library for proper list styling 2023-02-24 23:16:19 -05:00
Danny Avila 34ebd97c9d indicate initial gpt response 2023-02-24 16:31:59 -05:00
Danny Avila 61d898c387 fix bing chat not persisting after 1st invocation 2023-02-24 16:20:23 -05:00
Daniel Avila fd01fd3334 adds markdown support, fix code wrapping 2023-02-24 11:24:09 -05:00
Daniel Avila c58a9bbe93 finish highlight.js styling (for chatgpt) 2023-02-23 23:56:55 -05:00
Danny Avila e95e22de15 code highlighting in progress 2023-02-23 16:32:08 -05:00
Daniel Avila 187f7b5b03 handles in-line code in model response text (not multi-line yet) 2023-02-22 22:42:22 -05:00
Daniel Avila 1a6cddb8bb persist models in localStorage, minor changes 2023-02-22 21:30:48 -05:00
Daniel Avila e2a3ecbf88 update readme 2023-02-21 21:41:30 -05:00
Daniel Avila a516b38e27 adds linting, fix linter errors, abort automatic scrolling 2023-02-21 21:31:36 -05:00
Daniel Avila 16932b37c0 fix bingai bugs and add chatgptbrowser client 2023-02-21 21:30:56 -05:00
Daniel Avila 168d5e8075 fix linter errors 2023-02-21 19:40:28 -05:00
Daniel Avila 7b7548c905 persist bing ai conversation 2023-02-20 21:52:08 -05:00
Daniel Avila 81e52fed73 properly records bing conversations 2023-02-20 21:16:40 -05:00
Daniel Avila b4e22936ba update schema for bing options 2023-02-19 21:06:21 -05:00
Danny Avila ac2e897709 adds og bing icon 2023-02-16 14:06:18 -05:00
Danny Avila 5c7000c273 adds bing ai functionality, still need to code out message/convo storing since it returns a lot of useful parameters 2023-02-15 12:29:56 -05:00
Daniel Avila e0a26f52cd update readme 2023-02-15 00:06:10 -05:00
Daniel Avila f18317f7b7 useDocumentTitle changes title on first title change, scrollBar renders when expected 2023-02-14 21:23:14 -05:00
Daniel Avila 7347eda219 update readme 2023-02-14 19:46:24 -05:00
Daniel Avila e1b615835a new demo gif 2023-02-14 19:38:28 -05:00
Daniel Avila eaa0242e10 new demo gif 2023-02-14 19:11:47 -05:00
Daniel Avila 481927c5a3 adds demo gif 2023-02-14 19:03:48 -05:00
Daniel Avila 0833d795bd readme update and footer link update 2023-02-14 18:40:00 -05:00
Daniel Avila 7d7a4dd905 proxy server is down 2023-02-14 18:34:46 -05:00
Daniel Avila e520973203 draft readme 2023-02-14 18:31:29 -05:00
Danny Avila 9c225a9bb3 drafting readme 2023-02-14 16:33:22 -05:00
Danny Avila 742073fb89 refactor convos fetching to nav components, results in faster loading 2023-02-14 16:15:45 -05:00
Daniel Avila e4d1ff2523 rearrange convo fetching for performance in progress 2023-02-13 23:14:35 -05:00
Daniel Avila c00a2c902b front and backend logic for model switching 2023-02-13 21:15:28 -05:00
Daniel Avila a5afd5c48f animates scroll to bottom and debounces bottom 2023-02-13 20:13:59 -05:00
Daniel Avila 779f142058 fix static flag bug 2023-02-13 18:02:29 -05:00
Danny Avila 92b2109dc3 adds scrolltobottom button, still handling initial state when scrollbar is present 2023-02-13 16:31:18 -05:00
Danny Avila 583fd56ca5 adds shadcn-ui components, and model button to switch chat models 2023-02-13 15:58:35 -05:00
Danny Avila acaef39d12 refactors server route for brevity, state working, styling matching to chatgpt 2023-02-13 13:32:54 -05:00
Danny Avila dcf4f2a524 debugging convo state, useSWR now within convos component 2023-02-13 10:20:21 -05:00
Daniel Avila 0e5c7c7721 fixing convo state in progress 2023-02-12 22:57:59 -05:00
Daniel Avila 500b6c9fa8 adds functionality for waylaidwanderer chatgpt client 2023-02-12 22:24:36 -05:00
Daniel Avila 5b8c4452cc remove unused packages and minor changes 2023-02-12 16:48:15 -05:00
Daniel Avila ed44daf8b3 refactor express to use routes 2023-02-12 16:38:33 -05:00
Daniel Avila cd71bb0727 remove nodemon from server script 2023-02-11 17:35:07 -05:00
Daniel Avila 48c591a8b9 scrollbar styling 2023-02-11 13:48:48 -05:00
Daniel Avila 8eeac36b35 update press release 2023-02-11 13:05:48 -05:00
Daniel Avila 4cecbcf642 update press release 2023-02-11 13:02:30 -05:00
Daniel Avila 8432cbd293 update press release 2023-02-11 13:01:58 -05:00
Daniel Avila 256b60435f update press release, minor change in textchat component, remove unused package 2023-02-11 13:01:03 -05:00
Daniel Avila d9d5f4a509 delegate footer to own component 2023-02-11 12:54:02 -05:00
Daniel Avila e8c514fff9 delete updates state title 2023-02-11 12:49:07 -05:00
Daniel Avila ef4ec39a76 update press release notes 2023-02-11 11:54:17 -05:00
Daniel Avila 4304597cb4 small detail for renaming convos 2023-02-11 11:54:03 -05:00
Daniel Avila 7dd4e78bbf tests scrolling to top, show/hide templates 2023-02-11 11:37:20 -05:00
Daniel Avila 5af5a97d8f complete renaming functions, abstracts more svg, sets title to current convo title, adds a try again feature to errors 2023-02-11 10:22:15 -05:00
Daniel Avila 592b7629aa abstracts svgs, fiddling with prompt templates idea 2023-02-10 21:00:02 -05:00
Danny Avila cab4b70b7e took a screenshot of use case 2023-02-09 16:21:36 -05:00
Daniel Avila c36f752551 fix scrolling bug 2023-02-08 22:58:24 -05:00
Danny Avila 217bdb4865 client error handling, regen response without saving messages 2023-02-08 15:26:42 -05:00
Danny Avila 87685c3791 conversation conditional rendering 2023-02-08 11:05:54 -05:00
Danny Avila 0c6dc8665b dark mode alternates to light mode on dark theme, error state in progress 2023-02-08 10:49:38 -05:00
Danny Avila 5cd50e7826 handle textarea text state with redux and setText on example click 2023-02-08 09:59:01 -05:00
Danny Avila 581ee0e2ca adds favicon to head 2023-02-08 09:46:26 -05:00
Danny Avila d07047722e adds favicon to head 2023-02-08 09:44:10 -05:00
Danny Avila b5042a738e adds dark mode and logs errors in message response 2023-02-08 09:15:47 -05:00
Danny Avila 58498ed951 clears conversations and no new line on enter 2023-02-08 08:27:23 -05:00
Daniel Avila 51d93da3f8 adds server and client error handling, clear convos in progress 2023-02-08 00:02:29 -05:00
Daniel Avila 511ac948b4 clear all convos in progress, async code refactored in debugging warning 2023-02-07 19:07:48 -05:00
Daniel Avila 053368646d useeffect triggers react warning 'expected static flag was missing' but workarounds don't look as nice 2023-02-07 19:07:12 -05:00
Danny Avila 6842ac880c optimistic ui for message sending and submit state 2023-02-07 16:22:35 -05:00
Danny Avila 9d41ed4615 reorganize components and add root import plugin 2023-02-07 10:26:19 -05:00
Danny Avila faf8800e67 remove sourcemap error 2023-02-07 09:59:45 -05:00
Danny Avila 36ac055ae5 Complete rewire state to redux 2023-02-07 09:41:54 -05:00
Daniel Avila 7978ddd871 adding redux in progress 2023-02-07 00:05:00 -05:00
Daniel Avila 85efaa4173 adding redux to reduce prop drilling and allow for advanced conversation state 2023-02-06 21:18:28 -05:00
Daniel Avila 6e3f63ee46 adding clear methods for convos 2023-02-06 21:17:46 -05:00
Daniel Avila c7c50dbbab convo successfully changes on convo click 2023-02-06 18:25:11 -05:00
Danny Avila d35ed31b20 convo selection handling in progress 2023-02-06 16:28:50 -05:00
Danny Avila c794287ced load convos on message send & other conditional rendering 2023-02-06 16:00:59 -05:00
Danny Avila 2869638cc0 get convos endpoint 2023-02-06 15:17:54 -05:00
Danny Avila c821d1eee5 made model method one line shorter 2023-02-06 14:58:43 -05:00
Danny Avila 4e6b6be15c titles conversations based on first two messages 2023-02-06 14:57:47 -05:00
Danny Avila 511c8c9599 saves all conversations with messages 2023-02-06 14:05:02 -05:00
Danny Avila 232a823b9b store all messages to mongoDB 2023-02-06 13:28:21 -05:00
Danny Avila f2003da779 layout mimics actual chatgpt ui 2023-02-06 13:27:28 -05:00
Daniel Avila 254f9d7e26 adds db connectivity and saving, need to process user messages still 2023-02-05 23:05:07 -05:00
Daniel Avila f889f23792 chatgpt is taking shape, convo persists, layout mimics original 2023-02-05 19:41:24 -05:00
Daniel Avila 3a199757ae sse events on text submit 2023-02-05 15:29:35 -05:00
Daniel Avila 27f7276fc8 working SSE stream 2023-02-05 15:26:59 -05:00
Daniel Avila 81866f197a webpack config for polyfills 2023-02-04 20:49:58 -05:00
Daniel Avila 502bdc5dd5 building potential Server side events 2023-02-04 20:49:42 -05:00
Daniel Avila 641dcfad73 chat methods defined, also test file for library testing 2023-02-04 20:49:01 -05:00
Daniel Avila 7c4e7ab07a abstracts textarea 2023-02-04 20:48:33 -05:00
Daniel Avila 7b9f3ee9c2 update env variables and build 2023-02-04 20:09:58 -05:00
Daniel Avila 39781f5318 testing chatgpt logic 2023-02-04 20:09:02 -05:00
Daniel Avila 521a8eb704 templating model 2023-02-04 20:08:46 -05:00
Daniel Avila 92860a1bb4 adds express server 2023-02-04 20:08:28 -05:00
Daniel Avila 037a12a282 logs textarea input 2023-02-04 20:07:37 -05:00
Daniel Avila 5dc2d5efba updates to react 18 method createRoot 2023-02-04 20:07:08 -05:00
Daniel Avila 21a6a0587c adds basic layout 2023-02-04 19:19:53 -05:00
Daniel Avila 0fb3a1cab3 adds tailwindcss 2023-02-04 18:17:47 -05:00
Daniel Avila 74232d7671 initial commit, project is ready to start 2023-02-04 17:17:17 -05:00
hackreactor-bot 466dd01327 (init) 2022-10-20 11:45:11 +00:00
2576 changed files with 50552 additions and 916097 deletions
-2661
View File
File diff suppressed because it is too large Load Diff
-13
View File
@@ -1,13 +0,0 @@
# used by CI/CD testing
openai==1.67.0
python-dotenv
tiktoken
importlib_metadata
cohere
redis
anthropic
orjson==3.9.15
pydantic==2.7.1
google-cloud-aiplatform==1.43.0
fastapi-sso==0.10.0
uvloop==0.21.0
-52
View File
@@ -1,52 +0,0 @@
{
"name": "Python 3.11",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/python:3.11-bookworm",
// https://github.com/devcontainers/images/tree/main/src/python
// https://mcr.microsoft.com/en-us/product/devcontainers/python/tags
// "build": {
// "dockerfile": "Dockerfile",
// "context": ".."
// },
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
"settings": {},
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"GitHub.copilot",
"GitHub.copilot-chat",
"ms-python.autopep8"
]
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [4000],
"containerEnv": {
"LITELLM_LOG": "DEBUG"
},
// Use 'portsAttributes' to set default properties for specific forwarded ports.
// More info: https://containers.dev/implementors/json_reference/#port-attributes
"portsAttributes": {
"4000": {
"label": "LiteLLM Server",
"onAutoForward": "notify"
}
},
// More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "litellm",
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "pipx install poetry && poetry install -E extra_proxy -E proxy"
}
+2 -12
View File
@@ -1,12 +1,2 @@
docs
cookbook
.circleci
.github
tests
.git
.github
.circleci
.devcontainer
*.tgz
log.txt
docker/Dockerfile.*
**/node_modules
api/.env
-27
View File
@@ -1,27 +0,0 @@
# OpenAI
OPENAI_API_KEY = ""
OPENAI_API_BASE = ""
# Cohere
COHERE_API_KEY = ""
# OpenRouter
OR_SITE_URL = ""
OR_APP_NAME = "LiteLLM Example app"
OR_API_KEY = ""
# Azure API base URL
AZURE_API_BASE = ""
# Azure API version
AZURE_API_VERSION = ""
# Azure API key
AZURE_API_KEY = ""
# Replicate
REPLICATE_API_KEY = ""
REPLICATE_API_TOKEN = ""
# Anthropic
ANTHROPIC_API_KEY = ""
# Infisical
INFISICAL_TOKEN = ""
# Development Configs
LITELLM_MASTER_KEY = "sk-1234"
DATABASE_URL = "postgresql://llmproxy:dbpassword9090@db:5432/litellm"
STORE_MODEL_IN_DB = "True"
-46
View File
@@ -1,46 +0,0 @@
[flake8]
ignore =
# The following ignores can be removed when formatting using black
W191,W291,W292,W293,W391,W504
E101,E111,E114,E116,E117,E121,E122,E123,E124,E125,E126,E127,E128,E129,E131,
E201,E202,E221,E222,E225,E226,E231,E241,E251,E252,E261,E265,E271,E272,E275,
E301,E302,E303,E305,E306,
# line break before binary operator
W503,
# inline comment should start with '# '
E262,
# too many leading '#' for block comment
E266,
# multiple imports on one line
E401,
# module level import not at top of file
E402,
# Line too long (82 > 79 characters)
E501,
# comparison to None should be 'if cond is None:'
E711,
# comparison to True should be 'if cond is True:' or 'if cond:'
E712,
# do not compare types, for exact checks use `is` / `is not`, for instance checks use `isinstance()`
E721,
# do not use bare 'except'
E722,
# x is imported but unused
F401,
# 'from . import *' used; unable to detect undefined names
F403,
# x may be undefined, or defined from star imports:
F405,
# f-string is missing placeholders
F541,
# dictionary key '' repeated with different values
F601,
# redefinition of unused x from line 123
F811,
# undefined name x
F821,
# local variable x is assigned to but never used
F841,
# https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#flake8
extend-ignore = E203
-10
View File
@@ -1,10 +0,0 @@
# Add the commit hash of any commit you want to ignore in `git blame` here.
# One commit hash per line.
#
# The GitHub Blame UI will use this file automatically!
#
# Run this command to always ignore formatting commits in `git blame`
# git config blame.ignoreRevsFile .git-blame-ignore-revs
# Update pydantic code to fix warnings (GH-3600)
876840e9957bc7e9f7d6a2b58c4d7c53dad16481
-1
View File
@@ -1 +0,0 @@
*.ipynb linguist-vendored
+2 -2
View File
@@ -1,6 +1,6 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
github: [danny-avila]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
@@ -10,4 +10,4 @@ liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: https://buy.stripe.com/9AQ03Kd3P91o0Q8bIS
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
-49
View File
@@ -1,49 +0,0 @@
name: Bug Report
description: File a bug report
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us, what did you expect to happen?
placeholder: Tell us what you see!
value: "A bug happened!"
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell
- type: dropdown
id: ml-ops-team
attributes:
label: Are you a ML Ops Team?
description: This helps us prioritize your requests correctly
options:
- "No"
- "Yes"
validations:
required: true
- type: input
id: version
attributes:
label: What LiteLLM version are you on ?
placeholder: v1.53.1
validations:
required: true
- type: input
id: contact
attributes:
label: Twitter / LinkedIn details
description: We announce new features on Twitter + LinkedIn. If this issue leads to an announcement, and you'd like a mention, we'll gladly shout you out!
placeholder: ex. @krrish_dh / https://www.linkedin.com/in/krish-d/
validations:
required: false
-8
View File
@@ -1,8 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: Schedule Demo
url: https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat
about: Speak directly with Krrish and Ishaan, the founders, to discuss issues, share feedback, or explore improvements for LiteLLM
- name: Discord
url: https://discord.com/invite/wuPM9dRgDw
about: Join 250+ LiteLLM community members!
@@ -1,42 +0,0 @@
name: 🚀 Feature Request
description: Submit a proposal/request for a new LiteLLM feature.
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Thanks for making LiteLLM better!
- type: textarea
id: the-feature
attributes:
label: The Feature
description: A clear and concise description of the feature proposal
placeholder: Tell us what you want!
validations:
required: true
- type: textarea
id: motivation
attributes:
label: Motivation, pitch
description: Please outline the motivation for the proposal. Is your feature request related to a specific problem? e.g., "I'm working on X and would like Y to be possible". If this is related to another GitHub issue, please link here too.
validations:
required: true
- type: dropdown
id: ml-ops-team
attributes:
label: Are you a ML Ops Team?
description: This helps us prioritize your requests correctly
options:
- "No"
- "Yes"
validations:
required: true
- type: input
id: contact
attributes:
label: Twitter / LinkedIn details
description: We announce new features on Twitter + LinkedIn. When this is announced, and you'd like a mention, we'll gladly shout you out!
placeholder: ex. @krrish_dh / https://www.linkedin.com/in/krish-d/
validations:
required: false
@@ -1,77 +0,0 @@
name: Helm OCI Chart Releaser
description: Push Helm charts to OCI-based (Docker) registries
author: sergeyshaykhullin
branding:
color: yellow
icon: upload-cloud
inputs:
name:
required: true
description: Chart name
repository:
required: true
description: Chart repository name
tag:
required: true
description: Chart version
app_version:
required: true
description: App version
path:
required: false
description: Chart path (Default 'charts/{name}')
registry:
required: true
description: OCI registry
registry_username:
required: true
description: OCI registry username
registry_password:
required: true
description: OCI registry password
update_dependencies:
required: false
default: 'false'
description: Update chart dependencies before packaging (Default 'false')
outputs:
image:
value: ${{ steps.output.outputs.image }}
description: Chart image (Default '{registry}/{repository}/{image}:{tag}')
runs:
using: composite
steps:
- name: Helm | Login
shell: bash
run: echo ${{ inputs.registry_password }} | helm registry login -u ${{ inputs.registry_username }} --password-stdin ${{ inputs.registry }}
env:
HELM_EXPERIMENTAL_OCI: '1'
- name: Helm | Dependency
if: inputs.update_dependencies == 'true'
shell: bash
run: helm dependency update ${{ inputs.path == null && format('{0}/{1}', 'charts', inputs.name) || inputs.path }}
env:
HELM_EXPERIMENTAL_OCI: '1'
- name: Helm | Package
shell: bash
run: helm package ${{ inputs.path == null && format('{0}/{1}', 'charts', inputs.name) || inputs.path }} --version ${{ inputs.tag }} --app-version ${{ inputs.app_version }}
env:
HELM_EXPERIMENTAL_OCI: '1'
- name: Helm | Push
shell: bash
run: helm push ${{ inputs.name }}-${{ inputs.tag }}.tgz oci://${{ inputs.registry }}/${{ inputs.repository }}
env:
HELM_EXPERIMENTAL_OCI: '1'
- name: Helm | Logout
shell: bash
run: helm registry logout ${{ inputs.registry }}
env:
HELM_EXPERIMENTAL_OCI: '1'
- name: Helm | Output
id: output
shell: bash
run: echo "image=${{ inputs.registry }}/${{ inputs.repository }}/${{ inputs.name }}:${{ inputs.tag }}" >> $GITHUB_OUTPUT
-10
View File
@@ -1,10 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
groups:
github-actions:
patterns:
- "*"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

+72
View File
@@ -0,0 +1,72 @@
name: Playwright Tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
tests_e2e:
name: Run end-to-end tests
timeout-minutes: 60
runs-on: ubuntu-latest
env:
BINGAI_TOKEN: ${{ secrets.BINGAI_TOKEN }}
CHATGPT_TOKEN: ${{ secrets.CHATGPT_TOKEN }}
MONGO_URI: ${{ secrets.MONGO_URI }}
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'npm'
- name: Cache API dependencies
uses: actions/cache@v2
with:
path: ./api/node_modules
key: api-${{ runner.os }}-node-${{ hashFiles('./api/package-lock.json') }}
restore-keys: |
api-${{ runner.os }}-node-
- name: Install API dependencies
working-directory: ./api
run: npm ci
- name: Cache Client dependencies
uses: actions/cache@v2
with:
path: ./client/node_modules
key: client-${{ runner.os }}-node-${{ hashFiles('./client/package-lock.json') }}
restore-keys: |
client-${{ runner.os }}-node-
- name: Install Client dependencies
working-directory: ./client
run: npm ci
- name: Build Client
working-directory: ./client
run: npm run build
- name: Install global dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Start API server
working-directory: ./api
run: |
npm run start &
sleep 10 # Wait for the server to start
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: e2e/playwright-report/
retention-days: 30
-33
View File
@@ -1,33 +0,0 @@
## Title
<!-- e.g. "Implement user authentication feature" -->
## Relevant issues
<!-- e.g. "Fixes #000" -->
## Pre-Submission checklist
**Please complete all items before asking a LiteLLM maintainer to review your PR**
- [ ] I have Added testing in the [`tests/litellm/`](https://github.com/BerriAI/litellm/tree/main/tests/litellm) directory, **Adding at least 1 test is a hard requirement** - [see details](https://docs.litellm.ai/docs/extras/contributing_code)
- [ ] I have added a screenshot of my new test passing locally
- [ ] My PR passes all unit tests on (`make test-unit`)[https://docs.litellm.ai/docs/extras/contributing_code]
- [ ] My PR's scope is as isolated as possible, it only solves 1 specific problem
## Type
<!-- Select the type of Pull Request -->
<!-- Keep only the necessary ones -->
🆕 New Feature
🐛 Bug Fix
🧹 Refactoring
📖 Documentation
🚄 Infrastructure
✅ Test
## Changes
-94
View File
@@ -1,94 +0,0 @@
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
llmlite-service
SAM Template for llmlite-service
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 600
MemorySize: 128
Environment:
Variables:
WORKER_CONFIG: !Ref WorkerConfigParameter
Parameters:
AliasParameter:
Type: String
Default: live
WorkerConfigParameter:
Type: String
Description: Sample environment variable
Default: '{"model": null, "alias": null, "api_base": null, "api_version": "2023-07-01-preview", "debug": false, "temperature": null, "max_tokens": null, "request_timeout": 600, "max_budget": null, "telemetry": true, "drop_params": false, "add_function_to_prompt": false, "headers": null, "save": false, "config": null, "use_queue": false}'
Resources:
MyUrlFunctionPermissions:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref URL
Action: lambda:InvokeFunctionUrl
Principal: "*"
FunctionUrlAuthType: NONE
Function:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-function"
CodeUri: "./litellm"
Handler: proxy/lambda.handler
Runtime: python3.11
AutoPublishAlias: !Ref AliasParameter
Architectures:
- x86_64
DeploymentPreference:
Type: AllAtOnce
Alarms:
- !Ref NewVersionErrorMetricGreaterThanZeroAlarm
NewVersionErrorMetricGreaterThanZeroAlarm:
Type: "AWS::CloudWatch::Alarm"
Properties:
AlarmDescription: Lambda Function Error > 0
ComparisonOperator: GreaterThanThreshold
Dimensions:
- Name: Resource
Value: !Sub "${Function}:live"
- Name: FunctionName
Value: !Ref Function
- Name: ExecutedVersion
Value: !GetAtt Function.Version.Version
EvaluationPeriods: 1
Unit: Count
MetricName: Errors
Namespace: AWS/Lambda
Period: 60
Statistic: Sum
Threshold: 0
URL:
Type: AWS::Lambda::Url
DependsOn: FunctionAliaslive
Properties:
AuthType: NONE
Qualifier: live
TargetFunctionArn: !GetAtt Function.Arn
Outputs:
FunctionARN:
Description: "Lambda Function ARN"
Value: !GetAtt Function.Arn
FunctionUrl:
Description: "Lambda Function URL Endpoint"
Value:
Fn::GetAtt: URL.FunctionUrl
FunctionVersion:
Description: "Lambda Function Version"
Value: !GetAtt Function.Version.Version
FunctionNewAlarmARN:
Description: "Lambda Function New Alarm ARN"
Value: !GetAtt NewVersionErrorMetricGreaterThanZeroAlarm.Arn
+28
View File
@@ -0,0 +1,28 @@
name: Playwright Tests
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
tests_e2e:
name: Run end-to-end tests
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: e2e/playwright-report/
retention-days: 30
@@ -1,28 +0,0 @@
name: Updates model_prices_and_context_window.json and Create Pull Request
on:
schedule:
- cron: "0 0 * * 0" # Run every Sundays at midnight
#- cron: "0 0 * * *" # Run daily at midnight
jobs:
auto_update_price_and_context_window:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Dependencies
run: |
pip install aiohttp
- name: Update JSON Data
run: |
python ".github/workflows/auto_update_price_and_context_window_file.py"
- name: Create Pull Request
run: |
git add model_prices_and_context_window.json
git commit -m "Update model_prices_and_context_window.json file: $(date +'%Y-%m-%d')"
gh pr create --title "Update model_prices_and_context_window.json file" \
--body "Automated update for model_prices_and_context_window.json" \
--head auto-update-price-and-context-window-$(date +'%Y-%m-%d') \
--base main
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
@@ -1,121 +0,0 @@
import asyncio
import aiohttp
import json
# Asynchronously fetch data from a given URL
async def fetch_data(url):
try:
# Create an asynchronous session
async with aiohttp.ClientSession() as session:
# Send a GET request to the URL
async with session.get(url) as resp:
# Raise an error if the response status is not OK
resp.raise_for_status()
# Parse the response JSON
resp_json = await resp.json()
print("Fetch the data from URL.")
# Return the 'data' field from the JSON response
return resp_json['data']
except Exception as e:
# Print an error message if fetching data fails
print("Error fetching data from URL:", e)
return None
# Synchronize local data with remote data
def sync_local_data_with_remote(local_data, remote_data):
# Update existing keys in local_data with values from remote_data
for key in (set(local_data) & set(remote_data)):
local_data[key].update(remote_data[key])
# Add new keys from remote_data to local_data
for key in (set(remote_data) - set(local_data)):
local_data[key] = remote_data[key]
# Write data to the json file
def write_to_file(file_path, data):
try:
# Open the file in write mode
with open(file_path, "w") as file:
# Dump the data as JSON into the file
json.dump(data, file, indent=4)
print("Values updated successfully.")
except Exception as e:
# Print an error message if writing to file fails
print("Error updating JSON file:", e)
# Update the existing models and add the missing models
def transform_remote_data(data):
transformed = {}
for row in data:
# Add the fields 'max_tokens' and 'input_cost_per_token'
obj = {
"max_tokens": row["context_length"],
"input_cost_per_token": float(row["pricing"]["prompt"]),
}
# Add 'max_output_tokens' as a field if it is not None
if "top_provider" in row and "max_completion_tokens" in row["top_provider"] and row["top_provider"]["max_completion_tokens"] is not None:
obj['max_output_tokens'] = int(row["top_provider"]["max_completion_tokens"])
# Add the field 'output_cost_per_token'
obj.update({
"output_cost_per_token": float(row["pricing"]["completion"]),
})
# Add field 'input_cost_per_image' if it exists and is non-zero
if "pricing" in row and "image" in row["pricing"] and float(row["pricing"]["image"]) != 0.0:
obj['input_cost_per_image'] = float(row["pricing"]["image"])
# Add the fields 'litellm_provider' and 'mode'
obj.update({
"litellm_provider": "openrouter",
"mode": "chat"
})
# Add the 'supports_vision' field if the modality is 'multimodal'
if row.get('architecture', {}).get('modality') == 'multimodal':
obj['supports_vision'] = True
# Use a composite key to store the transformed object
transformed[f'openrouter/{row["id"]}'] = obj
return transformed
# Load local data from a specified file
def load_local_data(file_path):
try:
# Open the file in read mode
with open(file_path, "r") as file:
# Load and return the JSON data
return json.load(file)
except FileNotFoundError:
# Print an error message if the file is not found
print("File not found:", file_path)
return None
except json.JSONDecodeError as e:
# Print an error message if JSON decoding fails
print("Error decoding JSON:", e)
return None
def main():
local_file_path = "model_prices_and_context_window.json" # Path to the local data file
url = "https://openrouter.ai/api/v1/models" # URL to fetch remote data
# Load local data from file
local_data = load_local_data(local_file_path)
# Fetch remote data asynchronously
remote_data = asyncio.run(fetch_data(url))
# Transform the fetched remote data
remote_data = transform_remote_data(remote_data)
# If both local and remote data are available, synchronize and save
if local_data and remote_data:
sync_local_data_with_remote(local_data, remote_data)
write_to_file(local_file_path, local_data)
else:
print("Failed to fetch model data from either local file or URL.")
# Entry point of the script
if __name__ == "__main__":
main()
-389
View File
@@ -1,389 +0,0 @@
# this workflow is triggered by an API call when there is a new PyPI release of LiteLLM
name: Build, Publish LiteLLM Docker Image. New Release
on:
workflow_dispatch:
inputs:
tag:
description: "The tag version you want to build"
release_type:
description: "The release type you want to build. Can be 'latest', 'stable', 'dev'"
type: string
default: "latest"
commit_hash:
description: "Commit hash"
required: true
# Defines two custom environment variables for the workflow. Used for the Container registry domain, and a name for the Docker image that this workflow builds.
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
CHART_NAME: litellm-helm
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
jobs:
# print commit hash, tag, and release type
print:
runs-on: ubuntu-latest
steps:
- run: |
echo "Commit hash: ${{ github.event.inputs.commit_hash }}"
echo "Tag: ${{ github.event.inputs.tag }}"
echo "Release type: ${{ github.event.inputs.release_type }}"
docker-hub-deploy:
if: github.repository == 'BerriAI/litellm'
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.commit_hash }}
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: litellm/litellm:${{ github.event.inputs.tag || 'latest' }}
-
name: Build and push litellm-database image
uses: docker/build-push-action@v5
with:
context: .
push: true
file: ./docker/Dockerfile.database
tags: litellm/litellm-database:${{ github.event.inputs.tag || 'latest' }}
-
name: Build and push litellm-spend-logs image
uses: docker/build-push-action@v5
with:
context: .
push: true
file: ./litellm-js/spend-logs/Dockerfile
tags: litellm/litellm-spend_logs:${{ github.event.inputs.tag || 'latest' }}
build-and-push-image:
runs-on: ubuntu-latest
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.commit_hash }}
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# Configure multi platform Docker builds
- name: Set up QEMU
uses: docker/setup-qemu-action@e0e4588fad221d38ee467c0bffd91115366dc0c5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@edfb0fe6204400c56fbfd3feba3fe9ad1adfa345
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Build and push Docker image
uses: docker/build-push-action@4976231911ebf5f32aad765192d35f942aa48cb8
with:
context: .
push: true
tags: |
${{ steps.meta.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }},
${{ steps.meta.outputs.tags }}-${{ github.event.inputs.release_type }}
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm:main-{1}', env.REGISTRY, github.event.inputs.tag) || '' }},
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm:main-stable', env.REGISTRY) || '' }}
labels: ${{ steps.meta.outputs.labels }}
platforms: local,linux/amd64,linux/arm64,linux/arm64/v8
build-and-push-image-database:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.commit_hash }}
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for database Dockerfile
id: meta-database
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-database
# Configure multi platform Docker builds
- name: Set up QEMU
uses: docker/setup-qemu-action@e0e4588fad221d38ee467c0bffd91115366dc0c5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@edfb0fe6204400c56fbfd3feba3fe9ad1adfa345
- name: Build and push Database Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
file: ./docker/Dockerfile.database
push: true
tags: |
${{ steps.meta-database.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }},
${{ steps.meta-database.outputs.tags }}-${{ github.event.inputs.release_type }}
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm-database:main-{1}', env.REGISTRY, github.event.inputs.tag) || '' }},
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm-database:main-stable', env.REGISTRY) || '' }}
labels: ${{ steps.meta-database.outputs.labels }}
platforms: local,linux/amd64,linux/arm64,linux/arm64/v8
build-and-push-image-non_root:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.commit_hash }}
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for non_root Dockerfile
id: meta-non_root
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-non_root
# Configure multi platform Docker builds
- name: Set up QEMU
uses: docker/setup-qemu-action@e0e4588fad221d38ee467c0bffd91115366dc0c5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@edfb0fe6204400c56fbfd3feba3fe9ad1adfa345
- name: Build and push non_root Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
file: ./docker/Dockerfile.non_root
push: true
tags: |
${{ steps.meta-non_root.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }},
${{ steps.meta-non_root.outputs.tags }}-${{ github.event.inputs.release_type }}
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm-non_root:main-{1}', env.REGISTRY, github.event.inputs.tag) || '' }},
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm-non_root:main-stable', env.REGISTRY) || '' }}
labels: ${{ steps.meta-non_root.outputs.labels }}
platforms: local,linux/amd64,linux/arm64,linux/arm64/v8
build-and-push-image-spend-logs:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.commit_hash }}
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for spend-logs Dockerfile
id: meta-spend-logs
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-spend_logs
# Configure multi platform Docker builds
- name: Set up QEMU
uses: docker/setup-qemu-action@e0e4588fad221d38ee467c0bffd91115366dc0c5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@edfb0fe6204400c56fbfd3feba3fe9ad1adfa345
- name: Build and push Database Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
file: ./litellm-js/spend-logs/Dockerfile
push: true
tags: |
${{ steps.meta-spend-logs.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }},
${{ steps.meta-spend-logs.outputs.tags }}-${{ github.event.inputs.release_type }}
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm-spend_logs:main-{1}', env.REGISTRY, github.event.inputs.tag) || '' }},
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm-spend_logs:main-stable', env.REGISTRY) || '' }}
platforms: local,linux/amd64,linux/arm64,linux/arm64/v8
build-and-push-helm-chart:
if: github.event.inputs.release_type != 'dev'
needs: [docker-hub-deploy, build-and-push-image, build-and-push-image-database]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: lowercase github.repository_owner
run: |
echo "REPO_OWNER=`echo ${{github.repository_owner}} | tr '[:upper:]' '[:lower:]'`" >>${GITHUB_ENV}
- name: Get LiteLLM Latest Tag
id: current_app_tag
shell: bash
run: |
LATEST_TAG=$(git describe --tags --exclude "*dev*" --abbrev=0)
if [ -z "${LATEST_TAG}" ]; then
echo "latest_tag=latest" | tee -a $GITHUB_OUTPUT
else
echo "latest_tag=${LATEST_TAG}" | tee -a $GITHUB_OUTPUT
fi
- name: Get last published chart version
id: current_version
shell: bash
run: |
CHART_LIST=$(helm show chart oci://${{ env.REGISTRY }}/${{ env.REPO_OWNER }}/${{ env.CHART_NAME }} 2>/dev/null || true)
if [ -z "${CHART_LIST}" ]; then
echo "current-version=0.1.0" | tee -a $GITHUB_OUTPUT
else
printf '%s' "${CHART_LIST}" | grep '^version:' | awk 'BEGIN{FS=":"}{print "current-version="$2}' | tr -d " " | tee -a $GITHUB_OUTPUT
fi
env:
HELM_EXPERIMENTAL_OCI: '1'
# Automatically update the helm chart version one "patch" level
- name: Bump release version
id: bump_version
uses: christian-draeger/increment-semantic-version@1.1.0
with:
current-version: ${{ steps.current_version.outputs.current-version || '0.1.0' }}
version-fragment: 'bug'
- uses: ./.github/actions/helm-oci-chart-releaser
with:
name: ${{ env.CHART_NAME }}
repository: ${{ env.REPO_OWNER }}
tag: ${{ github.event.inputs.chartVersion || steps.bump_version.outputs.next-version || '0.1.0' }}
app_version: ${{ steps.current_app_tag.outputs.latest_tag }}
path: deploy/charts/${{ env.CHART_NAME }}
registry: ${{ env.REGISTRY }}
registry_username: ${{ github.actor }}
registry_password: ${{ secrets.GITHUB_TOKEN }}
update_dependencies: true
release:
name: "New LiteLLM Release"
needs: [docker-hub-deploy, build-and-push-image, build-and-push-image-database]
runs-on: "ubuntu-latest"
steps:
- name: Display version
run: echo "Current version is ${{ github.event.inputs.tag }}"
- name: "Set Release Tag"
run: echo "RELEASE_TAG=${{ github.event.inputs.tag }}" >> $GITHUB_ENV
- name: Display release tag
run: echo "RELEASE_TAG is $RELEASE_TAG"
- name: "Create release"
uses: "actions/github-script@v6"
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
script: |
const commitHash = "${{ github.event.inputs.commit_hash}}";
console.log("Commit Hash:", commitHash); // Add this line for debugging
try {
const response = await github.rest.repos.createRelease({
draft: false,
generate_release_notes: true,
target_commitish: commitHash,
name: process.env.RELEASE_TAG,
owner: context.repo.owner,
prerelease: false,
repo: context.repo.repo,
tag_name: process.env.RELEASE_TAG,
});
core.exportVariable('RELEASE_ID', response.data.id);
core.exportVariable('RELEASE_UPLOAD_URL', response.data.upload_url);
} catch (error) {
core.setFailed(error.message);
}
- name: Fetch Release Notes
id: release-notes
uses: actions/github-script@v6
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
script: |
try {
const response = await github.rest.repos.getRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: process.env.RELEASE_ID,
});
const formattedBody = JSON.stringify(response.data.body).slice(1, -1);
return formattedBody;
} catch (error) {
core.setFailed(error.message);
}
env:
RELEASE_ID: ${{ env.RELEASE_ID }}
- name: Github Releases To Discord
env:
WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }}
REALEASE_TAG: ${{ env.RELEASE_TAG }}
RELEASE_NOTES: ${{ steps.release-notes.outputs.result }}
run: |
curl -H "Content-Type: application/json" -X POST -d '{
"content": "New LiteLLM release '"${RELEASE_TAG}"'",
"username": "Release Changelog",
"avatar_url": "https://cdn.discordapp.com/avatars/487431320314576937/bd64361e4ba6313d561d54e78c9e7171.png",
"embeds": [
{
"title": "Changelog for LiteLLM '"${RELEASE_TAG}"'",
"description": "'"${RELEASE_NOTES}"'",
"color": 2105893
}
]
}' $WEBHOOK_URL
-67
View File
@@ -1,67 +0,0 @@
# this workflow is triggered by an API call when there is a new PyPI release of LiteLLM
name: Build, Publish LiteLLM Helm Chart. New Release
on:
workflow_dispatch:
inputs:
chartVersion:
description: "Update the helm chart's version to this"
# Defines two custom environment variables for the workflow. Used for the Container registry domain, and a name for the Docker image that this workflow builds.
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
REPO_OWNER: ${{github.repository_owner}}
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
jobs:
build-and-push-helm-chart:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: lowercase github.repository_owner
run: |
echo "REPO_OWNER=`echo ${{github.repository_owner}} | tr '[:upper:]' '[:lower:]'`" >>${GITHUB_ENV}
- name: Get LiteLLM Latest Tag
id: current_app_tag
uses: WyriHaximus/github-action-get-previous-tag@v1.3.0
- name: Get last published chart version
id: current_version
shell: bash
run: helm show chart oci://${{ env.REGISTRY }}/${{ env.REPO_OWNER }}/litellm-helm | grep '^version:' | awk 'BEGIN{FS=":"}{print "current-version="$2}' | tr -d " " | tee -a $GITHUB_OUTPUT
env:
HELM_EXPERIMENTAL_OCI: '1'
# Automatically update the helm chart version one "patch" level
- name: Bump release version
id: bump_version
uses: christian-draeger/increment-semantic-version@1.1.0
with:
current-version: ${{ steps.current_version.outputs.current-version || '0.1.0' }}
version-fragment: 'bug'
- name: Lint helm chart
run: helm lint deploy/charts/litellm-helm
- uses: ./.github/actions/helm-oci-chart-releaser
with:
name: litellm-helm
repository: ${{ env.REPO_OWNER }}
tag: ${{ github.event.inputs.chartVersion || steps.bump_version.outputs.next-version || '0.1.0' }}
app_version: ${{ steps.current_app_tag.outputs.tag || 'latest' }}
path: deploy/charts/litellm-helm
registry: ${{ env.REGISTRY }}
registry_username: ${{ github.actor }}
registry_password: ${{ secrets.GITHUB_TOKEN }}
update_dependencies: true
-27
View File
@@ -1,27 +0,0 @@
name: Helm unit test
on:
pull_request:
push:
branches:
- main
jobs:
unit-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Helm 3.11.1
uses: azure/setup-helm@v1
with:
version: '3.11.1'
- name: Install Helm Unit Test Plugin
run: |
helm plugin install https://github.com/helm-unittest/helm-unittest --version v0.4.4
- name: Run unit tests
run:
helm unittest -f 'tests/*.yaml' deploy/charts/litellm-helm
-138
View File
@@ -1,138 +0,0 @@
import csv
import os
from github import Github
def interpret_results(csv_file):
with open(csv_file, newline="") as csvfile:
csvreader = csv.DictReader(csvfile)
rows = list(csvreader)
"""
in this csv reader
- Create 1 new column "Status"
- if a row has a median response time < 300 and an average response time < 300, Status = "Passed ✅"
- if a row has a median response time >= 300 or an average response time >= 300, Status = "Failed ❌"
- Order the table in this order Name, Status, Median Response Time, Average Response Time, Requests/s,Failures/s, Min Response Time, Max Response Time, all other columns
"""
# Add a new column "Status"
for row in rows:
median_response_time = float(
row["Median Response Time"].strip().rstrip("ms")
)
average_response_time = float(
row["Average Response Time"].strip().rstrip("s")
)
request_count = int(row["Request Count"])
failure_count = int(row["Failure Count"])
failure_percent = round((failure_count / request_count) * 100, 2)
# Determine status based on conditions
if (
median_response_time < 300
and average_response_time < 300
and failure_percent < 5
):
row["Status"] = "Passed ✅"
else:
row["Status"] = "Failed ❌"
# Construct Markdown table header
markdown_table = "| Name | Status | Median Response Time (ms) | Average Response Time (ms) | Requests/s | Failures/s | Request Count | Failure Count | Min Response Time (ms) | Max Response Time (ms) |"
markdown_table += (
"\n| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |"
)
# Construct Markdown table rows
for row in rows:
markdown_table += f"\n| {row['Name']} | {row['Status']} | {row['Median Response Time']} | {row['Average Response Time']} | {row['Requests/s']} | {row['Failures/s']} | {row['Request Count']} | {row['Failure Count']} | {row['Min Response Time']} | {row['Max Response Time']} |"
print("markdown table: ", markdown_table)
return markdown_table
def _get_docker_run_command_stable_release(release_version):
return f"""
\n\n
## Docker Run LiteLLM Proxy
```
docker run \\
-e STORE_MODEL_IN_DB=True \\
-p 4000:4000 \\
ghcr.io/berriai/litellm:litellm_stable_release_branch-{release_version}
```
"""
def _get_docker_run_command(release_version):
return f"""
\n\n
## Docker Run LiteLLM Proxy
```
docker run \\
-e STORE_MODEL_IN_DB=True \\
-p 4000:4000 \\
ghcr.io/berriai/litellm:main-{release_version}
```
"""
def get_docker_run_command(release_version):
if "stable" in release_version:
return _get_docker_run_command_stable_release(release_version)
else:
return _get_docker_run_command(release_version)
if __name__ == "__main__":
csv_file = "load_test_stats.csv" # Change this to the path of your CSV file
markdown_table = interpret_results(csv_file)
# Update release body with interpreted results
github_token = os.getenv("GITHUB_TOKEN")
g = Github(github_token)
repo = g.get_repo(
"BerriAI/litellm"
) # Replace with your repository's username and name
latest_release = repo.get_latest_release()
print("got latest release: ", latest_release)
print(latest_release.title)
print(latest_release.tag_name)
release_version = latest_release.title
print("latest release body: ", latest_release.body)
print("markdown table: ", markdown_table)
# check if "Load Test LiteLLM Proxy Results" exists
existing_release_body = latest_release.body
if "Load Test LiteLLM Proxy Results" in latest_release.body:
# find the "Load Test LiteLLM Proxy Results" section and delete it
start_index = latest_release.body.find("Load Test LiteLLM Proxy Results")
existing_release_body = latest_release.body[:start_index]
docker_run_command = get_docker_run_command(release_version)
print("docker run command: ", docker_run_command)
new_release_body = (
existing_release_body
+ docker_run_command
+ "\n\n"
+ "### Don't want to maintain your internal proxy? get in touch 🎉"
+ "\nHosted Proxy Alpha: https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat"
+ "\n\n"
+ "## Load Test LiteLLM Proxy Results"
+ "\n\n"
+ markdown_table
)
print("new release body: ", new_release_body)
try:
latest_release.update_release(
name=latest_release.tag_name,
message=new_release_body,
)
except Exception as e:
print(e)
-17
View File
@@ -1,17 +0,0 @@
name: Label ML Ops Team Issues
on:
issues:
types:
- opened
jobs:
add-mlops-label:
runs-on: ubuntu-latest
steps:
- name: Check if ML Ops Team is selected
uses: actions-ecosystem/action-add-labels@v1
if: contains(github.event.issue.body, '### Are you a ML Ops Team?') && contains(github.event.issue.body, 'Yes')
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: "mlops user request"
-59
View File
@@ -1,59 +0,0 @@
name: Test Locust Load Test
on:
workflow_run:
workflows: ["Build, Publish LiteLLM Docker Image. New Release"]
types:
- completed
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install PyGithub
- name: re-deploy proxy
run: |
echo "Current working directory: $PWD"
ls
python ".github/workflows/redeploy_proxy.py"
env:
LOAD_TEST_REDEPLOY_URL1: ${{ secrets.LOAD_TEST_REDEPLOY_URL1 }}
LOAD_TEST_REDEPLOY_URL2: ${{ secrets.LOAD_TEST_REDEPLOY_URL2 }}
working-directory: ${{ github.workspace }}
- name: Run Load Test
id: locust_run
uses: BerriAI/locust-github-action@master
with:
LOCUSTFILE: ".github/workflows/locustfile.py"
URL: "https://post-release-load-test-proxy.onrender.com/"
USERS: "20"
RATE: "20"
RUNTIME: "300s"
- name: Process Load Test Stats
run: |
echo "Current working directory: $PWD"
ls
python ".github/workflows/interpret_load_test.py"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
working-directory: ${{ github.workspace }}
- name: Upload CSV as Asset to Latest Release
uses: xresloader/upload-to-github-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
file: "load_test_stats.csv;load_test.html"
update_latest_release: true
tag_name: "load-test"
overwrite: true
-28
View File
@@ -1,28 +0,0 @@
from locust import HttpUser, task, between
class MyUser(HttpUser):
wait_time = between(1, 5)
@task
def chat_completion(self):
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer sk-8N1tLOOyH8TIxwOLahhIVg",
# Include any additional headers you may need for authentication, etc.
}
# Customize the payload with "model" and "messages" keys
payload = {
"model": "fake-openai-endpoint",
"messages": [
{"role": "system", "content": "You are a chat bot."},
{"role": "user", "content": "Hello, how are you?"},
],
# Add more data as necessary
}
# Make a POST request to the "chat/completions" endpoint
response = self.client.post("chat/completions", json=payload, headers=headers)
# Print or log the response if needed
-34
View File
@@ -1,34 +0,0 @@
name: Publish Dev Release to PyPI
on:
workflow_dispatch:
jobs:
publish-dev-release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8 # Adjust the Python version as needed
- name: Install dependencies
run: pip install toml twine
- name: Read version from pyproject.toml
id: read-version
run: |
version=$(python -c 'import toml; print(toml.load("pyproject.toml")["tool"]["commitizen"]["version"])')
printf "LITELLM_VERSION=%s" "$version" >> $GITHUB_ENV
- name: Check if version exists on PyPI
id: check-version
run: |
set -e
if twine check --repository-url https://pypi.org/simple/ "litellm==$LITELLM_VERSION" >/dev/null 2>&1; then
echo "Version $LITELLM_VERSION already exists on PyPI. Skipping publish."
@@ -1,31 +0,0 @@
name: Read Version from pyproject.toml
on:
push:
branches:
- main # Change this to the default branch of your repository
jobs:
read-version:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8 # Adjust the Python version as needed
- name: Install dependencies
run: pip install toml
- name: Read version from pyproject.toml
id: read-version
run: |
version=$(python -c 'import toml; print(toml.load("pyproject.toml")["tool"]["commitizen"]["version"])')
printf "LITELLM_VERSION=%s" "$version" >> $GITHUB_ENV
- name: Display version
run: echo "Current version is $LITELLM_VERSION"
-20
View File
@@ -1,20 +0,0 @@
"""
redeploy_proxy.py
"""
import os
import requests
import time
# send a get request to this endpoint
deploy_hook1 = os.getenv("LOAD_TEST_REDEPLOY_URL1")
response = requests.get(deploy_hook1, timeout=20)
deploy_hook2 = os.getenv("LOAD_TEST_REDEPLOY_URL2")
response = requests.get(deploy_hook2, timeout=20)
print("SENT GET REQUESTS to re-deploy proxy")
print("sleeeping.... for 60s")
time.sleep(60)
-39
View File
@@ -1,39 +0,0 @@
name: Reset litellm_stable branch
on:
release:
types: [published, created]
jobs:
update-stable-branch:
if: ${{ startsWith(github.event.release.tag_name, 'v') && !endsWith(github.event.release.tag_name, '-stable') }}
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Reset litellm_stable_release_branch branch to the release commit
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Configure Git user
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Fetch all branches and tags
git fetch --all
# Check if the litellm_stable_release_branch branch exists
if git show-ref --verify --quiet refs/remotes/origin/litellm_stable_release_branch; then
echo "litellm_stable_release_branch branch exists."
git checkout litellm_stable_release_branch
else
echo "litellm_stable_release_branch branch does not exist. Creating it."
git checkout -b litellm_stable_release_branch
fi
# Reset litellm_stable_release_branch branch to the release commit
git reset --hard $GITHUB_SHA
# Push the updated litellm_stable_release_branch branch
git push origin litellm_stable_release_branch --force
-27
View File
@@ -1,27 +0,0 @@
Date,"Ben
Ashley",Tom Brooks,Jimmy Cooney,"Sue
Daniels",Berlinda Fong,Terry Jones,Angelina Little,Linda Smith
10/1,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE
10/2,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/3,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/4,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/5,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/6,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/7,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/8,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/9,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/10,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/11,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/12,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/13,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/14,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/15,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/16,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/17,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/18,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/19,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/20,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/21,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/22,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
10/23,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE
Total,0,1,1,1,1,1,0,1
1 Date Ben Ashley Tom Brooks Jimmy Cooney Sue Daniels Berlinda Fong Terry Jones Angelina Little Linda Smith
2 10/1 FALSE TRUE TRUE TRUE TRUE TRUE FALSE TRUE
3 10/2 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
4 10/3 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
5 10/4 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
6 10/5 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
7 10/6 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
8 10/7 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
9 10/8 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
10 10/9 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
11 10/10 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
12 10/11 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
13 10/12 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
14 10/13 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
15 10/14 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
16 10/15 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
17 10/16 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
18 10/17 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
19 10/18 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
20 10/19 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
21 10/20 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
22 10/21 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
23 10/22 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
24 10/23 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
25 Total 0 1 1 1 1 1 0 1
-20
View File
@@ -1,20 +0,0 @@
name: "Stale Issue Management"
on:
schedule:
- cron: '0 0 * * *' # Runs daily at midnight UTC
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v8
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
stale-issue-message: "This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs."
stale-pr-message: "This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs."
days-before-stale: 90 # Revert to 60 days
days-before-close: 7 # Revert to 7 days
stale-issue-label: "stale"
operations-per-run: 1000
-54
View File
@@ -1,54 +0,0 @@
import os
import requests
from datetime import datetime
# GitHub API endpoints
GITHUB_API_URL = "https://api.github.com"
REPO_OWNER = "BerriAI"
REPO_NAME = "litellm"
# GitHub personal access token (required for uploading release assets)
GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN")
# Headers for GitHub API requests
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {GITHUB_ACCESS_TOKEN}",
"X-GitHub-Api-Version": "2022-11-28",
}
# Get the latest release
releases_url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/releases/latest"
response = requests.get(releases_url, headers=headers)
latest_release = response.json()
print("Latest release:", latest_release)
# Upload an asset to the latest release
upload_url = latest_release["upload_url"].split("{?")[0]
asset_name = "results_stats.csv"
asset_path = os.path.join(os.getcwd(), asset_name)
print("upload_url:", upload_url)
with open(asset_path, "rb") as asset_file:
asset_data = asset_file.read()
upload_payload = {
"name": asset_name,
"label": "Load test results",
"created_at": datetime.utcnow().isoformat() + "Z",
}
upload_headers = headers.copy()
upload_headers["Content-Type"] = "application/octet-stream"
upload_response = requests.post(
upload_url,
headers=upload_headers,
data=asset_data,
params=upload_payload,
)
if upload_response.status_code == 201:
print(f"Asset '{asset_name}' uploaded successfully to the latest release.")
else:
print(f"Failed to upload asset. Response: {upload_response.text}")
+63 -82
View File
@@ -1,85 +1,66 @@
.venv
### node etc ###
# Logs
data-node
meili_data
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Compiled Dirs (http://nodejs.org/api/addons.html)
build/
dist/
public/main.js
public/main.js.map
public/main.js.LICENSE.txt
client/public/main.js
client/public/main.js.map
client/public/main.js.LICENSE.txt
# Dependency directorys
# Deployed apps should consider commenting these lines out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules/
meili_data/
api/node_modules/
client/node_modules/
bower_components/
# Floobits
.floo
.floobit
.floo
.flooignore
# Environment
.npmrc
.env
.newenv
newenv/*
litellm/proxy/myenv/*
litellm_uuid.txt
__pycache__/
*.pyc
bun.lockb
**/.DS_Store
.aider*
litellm_results.jsonl
secrets.toml
.gitignore
litellm/proxy/litellm_secrets.toml
litellm/proxy/api_log.json
.idea/
router_config.yaml
litellm_server/config.yaml
litellm/proxy/_secret_config.yaml
.aws-sam/
litellm/tests/aiologs.log
litellm/tests/exception_data.txt
litellm/tests/config_*.yaml
litellm/tests/langfuse.log
langfuse.log
.langfuse.log
litellm/tests/test_custom_logger.py
litellm/tests/langfuse.log
litellm/tests/dynamo*.log
cache.json
api/data/
owner.yml
archive
.vscode/settings.json
litellm/proxy/log.txt
proxy_server_config_@.yaml
.gitignore
proxy_server_config_2.yaml
litellm/proxy/secret_managers/credentials.json
hosted_config.yaml
litellm/proxy/tests/node_modules
litellm/proxy/tests/package.json
litellm/proxy/tests/package-lock.json
ui/litellm-dashboard/.next
ui/litellm-dashboard/node_modules
ui/litellm-dashboard/next-env.d.ts
ui/litellm-dashboard/package.json
ui/litellm-dashboard/package-lock.json
deploy/charts/litellm/*.tgz
deploy/charts/litellm/charts/*
deploy/charts/*.tgz
litellm/proxy/vertex_key.json
**/.vim/
**/node_modules
kub.yaml
loadtest_kub.yaml
litellm/proxy/_new_secret_config.yaml
litellm/proxy/_new_secret_config.yaml
litellm/proxy/_super_secret_config.yaml
litellm/proxy/_super_secret_config.yaml
litellm/proxy/myenv/bin/activate
litellm/proxy/myenv/bin/Activate.ps1
myenv/*
litellm/proxy/_experimental/out/404/index.html
litellm/proxy/_experimental/out/model_hub/index.html
litellm/proxy/_experimental/out/onboarding/index.html
litellm/tests/log.txt
litellm/tests/langfuse.log
litellm/tests/langfuse.log
litellm/proxy/google-cloud-sdk/*
tests/llm_translation/log.txt
venv/
tests/local_testing/log.txt
src/style - official.css
/e2e/specs/.test-results/
/e2e/playwright-report/
/playwright/.cache/
.DS_Store
*.code-workspace
# meilisearch
meilisearch
data.ms/*
.codegpt
litellm/proxy/_new_new_secret_config.yaml
litellm/proxy/custom_guardrail.py
litellm/proxy/_experimental/out/404.html
litellm/proxy/_experimental/out/404.html
litellm/proxy/_experimental/out/model_hub.html
.mypy_cache/*
litellm/proxy/application.log
tests/llm_translation/vertex_test_account.json
tests/llm_translation/test_vertex_key.json
litellm/proxy/migrations/0_init/migration.sql
litellm/proxy/db/migrations/0_init/migration.sql
litellm/proxy/db/migrations/*
litellm/proxy/migrations/*
-49
View File
@@ -1,49 +0,0 @@
repos:
- repo: local
hooks:
- id: pyright
name: pyright
entry: pyright
language: system
types: [python]
files: ^litellm/
- id: isort
name: isort
entry: isort
language: system
types: [python]
files: litellm/.*\.py
exclude: ^litellm/__init__.py$
- repo: https://github.com/psf/black
rev: 24.2.0
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 7.0.0 # The version of flake8 to use
hooks:
- id: flake8
exclude: ^litellm/tests/|^litellm/proxy/tests/|^litellm/tests/litellm/|^tests/litellm/
additional_dependencies: [flake8-print]
files: litellm/.*\.py
# - id: flake8
# name: flake8 (router.py function length)
# files: ^litellm/router\.py$
# args: [--max-function-length=40]
# # additional_dependencies: [flake8-functions]
- repo: https://github.com/python-poetry/poetry
rev: 1.8.0
hooks:
- id: poetry-check
- repo: local
hooks:
- id: check-files-match
name: Check if files match
entry: python3 ci_cd/check_files_match.py
language: system
# - id: check-file-length
# name: Check file length
# entry: python check_file_length.py
# args: ["10000"] # set your desired maximum number of lines
# language: python
# files: litellm/.*\.py
# exclude: ^litellm/tests/
+83
View File
@@ -0,0 +1,83 @@
# # Changelog
<details open>
<summary><strong>2023-05-11</strong></summary>
**Released [v0.4.2](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.2)**
ChatGPT-Clone received some important upgrades and improvements. A new contributor, [@qcgm1978](https://github.com/qcgm1978), makes their first contribution by adding a null check for adaptiveCards variable. Additionally, support for titling conversations with the Azure endpoint is added by [@danny-avila](https://github.com/danny-avila) in PR [#234](https://github.com/danny-avila/chatgpt-clone/pull/234). In PR [#235](https://github.com/danny-avila/chatgpt-clone/pull/235), [@danny-avila](https://github.com/danny-avila) also makes some necessary fixes to titling, quotation marks, and endpoints being unavailable with only the Azure key provided. The logging system is now powered by Pino and sanitization, thanks to [@danorlando](https://github.com/danorlando) in PR [#227](https://github.com/danny-avila/chatgpt-clone/pull/227). To bulletproof the Docker container, the .dockerignore file is updated to include the client/.env file by [@danny-avila](https://github.com/danny-avila) in PR [#241](https://github.com/danny-avila/chatgpt-clone/pull/241). This issue was brought to our attention on discord.
There is active work on the new Plugins feature, converting the frontend to Typescript, and looking to integrate Palm2, google's new generative AI accessible via API, to the project as a new endpoint.
You can check the full changelog in between [v0.4.1](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.1) and [v0.4.2](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.2) [here](https://github.com/danny-avila/chatgpt-clone/compare/v0.4.1...v0.4.2)."
For discussion and suggestion you can join us: **[community discord server](https://discord.gg/NGaa9RPCft)**
</details>
<details>
<summary><strong>2023-05-09</strong></summary>
**Released [v0.4.1](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.1)**
* update user system section of readme by @danorlando in #207
* remove github-passport and update package.lock files by @danorlando in #208
* Update README.md by @fuegovic in #209
* fix: fix browser refresh redirecting to /chat/new by @danorlando in #210
* fix: fix issue with validation when google account has multiple spaces in username by @danorlando in #211
* chore: update docker image version to use latest by @danny-avila in #218
* update documentation structure by @fuegovic in #220
* Feat: Add Azure support by @danny-avila in #219
* Update Message.js by @DavidDev1334 in #191
⚠️ **IMPORTANT :** Since V0.4.0 You should register and login with a local account (email and password) for the first time sign-up. if you use login for the first time with a social login account (eg. Google, facebook, etc.), the conversations and presets that you created before the user system was implemented will NOT be migrated to that account.
⚠️ **Breaking - new Env Variables :** Since V0.4.0 You will need to add the new env variables from .env.example for the app to work, even if you're not using multiple users for your purposes.
For discussion and suggestion you can join us: **[community discord server](https://discord.gg/NGaa9RPCft)**
</details>
<details>
<summary><strong>2023-05-07</strong></summary>
**Released [v0.4.0](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.0)**, Introducing User/Auth System and OAuth2/Social Login! You can now register and login with an email account or use Google login. Your your previous conversations and presets will migrate to your new profile upon creation. Check out the details in the [User/Auth System](#userauth-system) section of the README.md.
⚠️ **IMPORTANT :** You should register and login with a local account (email and password) for the first time sign-up. if you use login for the first time with a social login account (eg. Google, facebook, etc.), the conversations and presets that you created before the user system was implemented will NOT be migrated to that account.
⚠️ **Breaking - new Env Variables :** You will need to add the new env variables from .env.example for the app to work, even if you're not using multiple users for your purposes.
For discussion and suggestion you can join us: **[community discord server](https://discord.gg/NGaa9RPCft)**
</details>
<details>
<summary><strong>2023-04-05</strong></summary>
**Released [v0.3.0](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.3.0)**, Introducing more customization for both OpenAI & BingAI conversations! This is one of the biggest updates yet and will make integrating future LLM's a lot easier, providing a lot of customization features as well, including sharing presets! Please feel free to share them in the **[community discord server](https://discord.gg/NGaa9RPCft)**
</details>
<details>
<summary><strong>2023-03-23</strong></summary>
**Released [v0.1.0](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.1.0)**, **searching messages/conversations is live!** Up next is more custom parameters for customGpt's. Join the discord server for more immediate assistance and update: **[community discord server](https://discord.gg/NGaa9RPCft)**
</details>
<details>
<summary><strong>2023-03-22</strong></summary>
**Released [v0.0.6](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.0.6)**, the latest stable release before **Searching messages** goes live tomorrow. See exact updates to date in the tag link. By request, there is now also a **[community discord server](https://s
</details>
<details>
<summary><strong>2023-03-20</strong></summary>
**Searching messages** is almost here as I test more of its functionality. There've been a lot of great features requested and great contributions and I will work on some soon, namely, further customizing the custom gpt params with sliders similar to the OpenAI playground, and including the custom params and system messages available to Bing.
The above features are next and then I will have to focus on building the **test environment.** I would **greatly appreciate** help in this area with any test environment you're familiar with (mocha, chai, jest, playwright, puppeteer). This is to aid in the velocity of contributing and to save time I spend debugging.
On that note, I had to switch the default branch due to some breaking changes that haven't been straight forward to debug, mainly related to node-chat-gpt the main dependency of the project. Thankfully, my working branch, now switched to default as main, is working as expected.
</details>
##
## [Go Back to ReadMe](README.md)
+26
View File
@@ -0,0 +1,26 @@
# Contributors List
We appreciate all the contributors who helped make this project possible:
- danny-avila (Admin)
- wtlyu (Contributor)
- danorlando (Contributor)
- alfredo-f (Contributor)
- HyunggyuJang (Contributor)
- fuegovic (Contributor)
- DavidDev1334
- toordog (Contributor)
- heathriel (External Contributor)
- hackreactor-bot (Contributor)
- git-bruh (Contributor)
- zhangsean (Contributor)
- llk89 (Contributor)
- adamrb (Contributor)
If you have contributed to this project and would like to be added to the list of contributors, please submit a pull request updating this file with your name and GitHub username.
##
## [Go Back to ReadMe](README.md)
+36 -81
View File
@@ -1,83 +1,38 @@
# Base image for building
ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/python:latest-dev
FROM node:19-alpine AS react-client
WORKDIR /client
# copy package.json into the container at /client
COPY /client/.env /client/.env
COPY /client/package*.json /client/
# install dependencies
RUN npm ci
# Copy the current directory contents into the container at /client
COPY /client/ /client/
# Set the memory limit for Node.js
ENV NODE_OPTIONS="--max-old-space-size=2048"
# Build artifacts
RUN npm run build
# Runtime image
ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/python:latest-dev
# Builder stage
FROM $LITELLM_BUILD_IMAGE AS builder
FROM node:19-alpine AS node-api
WORKDIR /api
# copy package.json into the container at /api
COPY /api/package*.json /api/
# install dependencies
RUN npm ci
# Copy the current directory contents into the container at /api
COPY /api/ /api/
# Copy the client side code
COPY --from=react-client /client/dist /client/dist
# Make port 3080 available to the world outside this container
EXPOSE 3080
# Expose the server to 0.0.0.0
ENV HOST=0.0.0.0
# Run the app when the container launches
CMD ["npm", "start"]
# Set the working directory to /app
WORKDIR /app
USER root
# Install build dependencies
RUN apk update && \
apk add --no-cache gcc python3-dev openssl openssl-dev
RUN pip install --upgrade pip && \
pip install build
# Copy the current directory contents into the container at /app
COPY . .
# Build Admin UI
RUN chmod +x docker/build_admin_ui.sh && ./docker/build_admin_ui.sh
# Build the package
RUN rm -rf dist/* && python -m build
# There should be only one wheel file now, assume the build only creates one
RUN ls -1 dist/*.whl | head -1
# Install the package
RUN pip install dist/*.whl
# install dependencies as wheels
RUN pip wheel --no-cache-dir --wheel-dir=/wheels/ -r requirements.txt
# install semantic-cache [Experimental]- we need this here and not in requirements.txt because redisvl pins to pydantic 1.0
RUN pip install redisvl==0.0.7 --no-deps
# ensure pyjwt is used, not jwt
RUN pip uninstall jwt -y
RUN pip uninstall PyJWT -y
RUN pip install PyJWT==2.9.0 --no-cache-dir
# Build Admin UI
RUN chmod +x docker/build_admin_ui.sh && ./docker/build_admin_ui.sh
# Runtime stage
FROM $LITELLM_RUNTIME_IMAGE AS runtime
# Ensure runtime stage runs as root
USER root
# Install runtime dependencies
RUN apk update && \
apk add --no-cache openssl
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY . .
RUN ls -la /app
# Copy the built wheel from the builder stage to the runtime stage; assumes only one wheel file is present
COPY --from=builder /app/dist/*.whl .
COPY --from=builder /wheels/ /wheels/
# Install the built wheel using pip; again using a wildcard if it's the only file
RUN pip install *.whl /wheels/* --no-index --find-links=/wheels/ && rm -f *.whl && rm -rf /wheels
# Generate prisma client
RUN prisma generate
RUN chmod +x docker/entrypoint.sh
RUN chmod +x docker/prod_entrypoint.sh
EXPOSE 4000/tcp
ENTRYPOINT ["docker/prod_entrypoint.sh"]
# Append "--detailed_debug" to the end of CMD to view detailed debug logs
CMD ["--port", "4000"]
# Optional: for client with nginx routing
FROM nginx:stable-alpine AS nginx-client
WORKDIR /usr/share/nginx/html
COPY --from=react-client /client/dist /usr/share/nginx/html
# Add your nginx.conf
COPY /client/nginx.conf /etc/nginx/conf.d/default.conf
ENTRYPOINT ["nginx", "-g", "daemon off;"]
+35
View File
@@ -0,0 +1,35 @@
# ./Dockerfile
FROM node:19-alpine
WORKDIR /app
# Copy package.json files for client and api
COPY /client/package*.json /app/client/
COPY /api/package*.json /app/api/
# Install dependencies for both client and api
RUN cd /app/client && npm ci
RUN cd /app/api && npm ci
# Copy the current directory contents into the container
COPY /client/ /app/client/
COPY /api/ /app/api/
# Set the memory limit for Node.js
ENV NODE_OPTIONS="--max-old-space-size=2048"
# Build artifacts for the client
RUN cd /app/client && npm run build
# Create the necessary directory and copy the client side code to the api directory
RUN mkdir -p /app/api/client && cp -R /app/client/dist /app/api/client/dist
# Make port 3080 available to the world outside this container
EXPOSE 3080
# Expose the server to 0.0.0.0
ENV HOST=0.0.0.0
# Run the app when the container launches
WORKDIR /app/api
CMD ["npm", "start"]
+9 -8
View File
@@ -1,12 +1,7 @@
Portions of this software are licensed as follows:
* All content that resides under the "enterprise/" directory of this repository, if that directory exists, is licensed under the license defined in "enterprise/LICENSE".
* Content outside of the above mentioned directories or restrictions above is available under the MIT license as defined below.
---
MIT License
Copyright (c) 2023 Berri AI
# MIT License
Copyright (c) 2023 Danny Avila
##
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
@@ -17,6 +12,8 @@ furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
##
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -24,3 +21,7 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
##
## [Go Back to ReadMe](README.md)
-32
View File
@@ -1,32 +0,0 @@
# LiteLLM Makefile
# Simple Makefile for running tests and basic development tasks
.PHONY: help test test-unit test-integration lint format
# Default target
help:
@echo "Available commands:"
@echo " make test - Run all tests"
@echo " make test-unit - Run unit tests"
@echo " make test-integration - Run integration tests"
@echo " make test-unit-helm - Run helm unit tests"
install-dev:
poetry install --with dev
lint: install-dev
poetry run pip install types-requests types-setuptools types-redis types-PyYAML
cd litellm && poetry run mypy . --ignore-missing-imports
# Testing
test:
poetry run pytest tests/
test-unit:
poetry run pytest tests/litellm/
test-integration:
poetry run pytest tests/ -k "not litellm"
test-unit-helm:
helm unittest -f 'tests/*.yaml' deploy/charts/litellm-helm
+124 -402
View File
@@ -1,413 +1,135 @@
<h1 align="center">
🚅 LiteLLM
</h1>
<p align="center">
<p align="center">
<a href="https://render.com/deploy?repo=https://github.com/BerriAI/litellm" target="_blank" rel="nofollow"><img src="https://render.com/images/deploy-to-render-button.svg" alt="Deploy to Render"></a>
<a href="https://railway.app/template/HLP0Ub?referralCode=jch2ME">
<img src="https://railway.app/button.svg" alt="Deploy on Railway">
</a>
</p>
<p align="center">Call all LLM APIs using the OpenAI format [Bedrock, Huggingface, VertexAI, TogetherAI, Azure, OpenAI, Groq etc.]
<br>
</p>
<h4 align="center"><a href="https://docs.litellm.ai/docs/simple_proxy" target="_blank">LiteLLM Proxy Server (LLM Gateway)</a> | <a href="https://docs.litellm.ai/docs/hosted" target="_blank"> Hosted Proxy (Preview)</a> | <a href="https://docs.litellm.ai/docs/enterprise"target="_blank">Enterprise Tier</a></h4>
<h4 align="center">
<a href="https://pypi.org/project/litellm/" target="_blank">
<img src="https://img.shields.io/pypi/v/litellm.svg" alt="PyPI Version">
</a>
<a href="https://dl.circleci.com/status-badge/redirect/gh/BerriAI/litellm/tree/main" target="_blank">
<img src="https://dl.circleci.com/status-badge/img/gh/BerriAI/litellm/tree/main.svg?style=svg" alt="CircleCI">
</a>
<a href="https://www.ycombinator.com/companies/berriai">
<img src="https://img.shields.io/badge/Y%20Combinator-W23-orange?style=flat-square" alt="Y Combinator W23">
</a>
<a href="https://wa.link/huol9n">
<img src="https://img.shields.io/static/v1?label=Chat%20on&message=WhatsApp&color=success&logo=WhatsApp&style=flat-square" alt="Whatsapp">
</a>
<a href="https://discord.gg/wuPM9dRgDw">
<img src="https://img.shields.io/static/v1?label=Chat%20on&message=Discord&color=blue&logo=Discord&style=flat-square" alt="Discord">
</a>
</h4>
<p align="center">
<a href="https://discord.gg/NGaa9RPCft">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/110412045/228325485-9d3e618f-a980-44fe-89e9-d6d39164680e.png">
<img src="https://user-images.githubusercontent.com/110412045/228325485-9d3e618f-a980-44fe-89e9-d6d39164680e.png" height="128">
</picture>
<h1 align="center">ChatGPT Clone</h1>
</a>
</p>
<p align="center">
<a aria-label="Join the community on Discord" href="https://discord.gg/NGaa9RPCft">
<img alt="" src="https://img.shields.io/badge/Join%20the%20community-blueviolet.svg?style=for-the-badge&logo=DISCORD&labelColor=000000&logoWidth=20">
</a>
<a aria-label="Sponsors" href="#sponsors">
<img alt="" src="https://img.shields.io/badge/SPONSORS-brightgreen.svg?style=for-the-badge&labelColor=000000&logoWidth=20">
</a>
</p>
## All AI Conversations under One Roof. ##
Assistant AIs are the future and OpenAI revolutionized this movement with ChatGPT. While numerous UIs exist, this app commemorates the original styling of ChatGPT, with the ability to integrate any current/future AI models, while integrating and improving upon original client features, such as conversation/message search and prompt templates (currently WIP). Through this clone, you can avoid ChatGPT Plus in favor of free or pay-per-call APIs. I will soon deploy a demo of this app. Feel free to contribute, clone, or fork. Currently dockerized.
![clone3](https://user-images.githubusercontent.com/110412045/230538752-9b99dc6e-cd02-483a-bff0-6c6e780fa7ae.gif)
# Features
- Response streaming identical to ChatGPT through server-sent events
- UI from original ChatGPT, including Dark mode
- AI model selection (through 3 endpoints: OpenAI API, BingAI, and ChatGPT Browser)
- Create, Save, & Share custom presets for OpenAI and BingAI endpoints - [More info on customization here](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.3.0)
- Edit and Resubmit messages just like the official site (with conversation branching)
- Search all messages/conversations - [More info here](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.1.0)
- Integrating plugins soon
##
# Sponsors
Sponsored by <a href="https://github.com/DavidDev1334"><b>@DavidDev1334</b></a>, <a href="https://github.com/mjtechguy"><b>@mjtechguy</b></a>, <a href="https://github.com/Pharrcyde"><b>@Pharrcyde</b></a>, & <a href="https://github.com/fuegovic"><b>@fuegovic</b></a>
##
<details open>
<summary><strong>2023-05-11</strong></summary>
**Released [v0.4.2](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.2)**
ChatGPT-Clone received some important upgrades and improvements. A new contributor, [@qcgm1978](https://github.com/qcgm1978), makes their first contribution by adding a null check for adaptiveCards variable. Additionally, support for titling conversations with the Azure endpoint is added by [@danny-avila](https://github.com/danny-avila) in PR [#234](https://github.com/danny-avila/chatgpt-clone/pull/234). In PR [#235](https://github.com/danny-avila/chatgpt-clone/pull/235), [@danny-avila](https://github.com/danny-avila) also makes some necessary fixes to titling, quotation marks, and endpoints being unavailable with only the Azure key provided. The logging system is now powered by Pino and sanitization, thanks to [@danorlando](https://github.com/danorlando) in PR [#227](https://github.com/danny-avila/chatgpt-clone/pull/227). To bulletproof the Docker container, the .dockerignore file is updated to include the client/.env file by [@danny-avila](https://github.com/danny-avila) in PR [#241](https://github.com/danny-avila/chatgpt-clone/pull/241). This issue was brought to our attention on discord.
There is active work on the new Plugins feature, converting the frontend to Typescript, and looking to integrate Palm2, google's new generative AI accessible via API, to the project as a new endpoint.
You can check the full changelog in between [v0.4.1](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.1) and [v0.4.2](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.2) [here](https://github.com/danny-avila/chatgpt-clone/compare/v0.4.1...v0.4.2)."
⚠️ **IMPORTANT :** Since V0.4.0 You should register and login with a local account (email and password) for the first time sign-up. if you use login for the first time with a social login account (eg. Google, facebook, etc.), the conversations and presets that you created before the user system was implemented will NOT be migrated to that account.
⚠️ **Breaking - new Env Variables :** Since V0.4.0 You will need to add the new env variables from .env.example for the app to work, even if you're not using multiple users for your purposes.
For discussion and suggestion you can join us: **[community discord server](https://discord.gg/NGaa9RPCft)**
</details>
[Past Updates](CHANGELOG.md)
##
<h1>Table of Contents</h1>
<details open>
<summary><strong>Getting Started</strong></summary>
* [Docker Install](/documents/install/docker_install.md)
* [Linux Install](documents/install/linux_install.md)
* [Mac Install](documents/install/mac_install.md)
* [Windows Install](documents/install/windows_install.md)
</details>
<details>
<summary><strong>General Information</strong></summary>
* [Project Origin](documents/general_info/project_origin.md)
* [Roadmap](documents/general_info/roadmap.md)
* [Tech Stack](documents/general_info/tech_stack.md)
* [Changelog](CHANGELOG.md)
* [Bing Jailbreak Info](documents/general_info/bing_jailbreak_info.md)
</details>
<details>
<summary><strong>Features</strong></summary>
* [User Auth System](documents/features/user_auth_system.md)
* [Proxy](documents/features/proxy.md)
</details>
<details>
<summary><strong>Cloud Deployment</strong></summary>
* [Heroku](documents/deployment/heroku.md)
</details>
<details>
<summary><strong>Contributions</strong></summary>
* [Code of Conduct](documents/contributions/code_of_conduct.md)
* [Contributor Guidelines](documents/contributions/contributor_guidelines.md)
* [Documentation Guidelines](documents/contributions/documentation_guidelines.md)
* [Testing](documents/contributions/testing.md)
* [Pull Request Template](documents/contributions/pull_request_template.md)
* [Contributors](CONTRIBUTORS.md)
* [Trello Board](https://trello.com/b/17z094kq/chatgpt-clone)
</details>
LiteLLM manages:
<details>
<summary><strong>Report Templates</strong></summary>
- Translate inputs to provider's `completion`, `embedding`, and `image_generation` endpoints
- [Consistent output](https://docs.litellm.ai/docs/completion/output), text responses will always be available at `['choices'][0]['message']['content']`
- Retry/fallback logic across multiple deployments (e.g. Azure/OpenAI) - [Router](https://docs.litellm.ai/docs/routing)
- Set Budgets & Rate limits per project, api key, model [LiteLLM Proxy Server (LLM Gateway)](https://docs.litellm.ai/docs/simple_proxy)
* [Bug Report Template](documents/report_templates/bug_report_template.md)
* [Custom Issue Template](documents/report_templates/custom_issue_template.md)
* [Feature Request Template](documents/report_templates/feature_request_template.md)
</details>
[**Jump to LiteLLM Proxy (LLM Gateway) Docs**](https://github.com/BerriAI/litellm?tab=readme-ov-file#openai-proxy---docs) <br>
[**Jump to Supported LLM Providers**](https://github.com/BerriAI/litellm?tab=readme-ov-file#supported-providers-docs)
##
### [Alternative Documentation](https://chatgpt-clone.gitbook.io/chatgpt-clone-docs/get-started/docker)
🚨 **Stable Release:** Use docker images with the `-stable` tag. These have undergone 12 hour load tests, before being published. [More information about the release cycle here](https://docs.litellm.ai/docs/proxy/release_cycle)
Support for more providers. Missing a provider or LLM Platform, raise a [feature request](https://github.com/BerriAI/litellm/issues/new?assignees=&labels=enhancement&projects=&template=feature_request.yml&title=%5BFeature%5D%3A+).
# Usage ([**Docs**](https://docs.litellm.ai/docs/))
> [!IMPORTANT]
> LiteLLM v1.0.0 now requires `openai>=1.0.0`. Migration guide [here](https://docs.litellm.ai/docs/migration)
> LiteLLM v1.40.14+ now requires `pydantic>=2.0.0`. No changes required.
<a target="_blank" href="https://colab.research.google.com/github/BerriAI/litellm/blob/main/cookbook/liteLLM_Getting_Started.ipynb">
<img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/>
</a>
```shell
pip install litellm
```
```python
from litellm import completion
import os
## set ENV variables
os.environ["OPENAI_API_KEY"] = "your-openai-key"
os.environ["ANTHROPIC_API_KEY"] = "your-anthropic-key"
messages = [{ "content": "Hello, how are you?","role": "user"}]
# openai call
response = completion(model="openai/gpt-4o", messages=messages)
# anthropic call
response = completion(model="anthropic/claude-3-sonnet-20240229", messages=messages)
print(response)
```
### Response (OpenAI Format)
```json
{
"id": "chatcmpl-565d891b-a42e-4c39-8d14-82a1f5208885",
"created": 1734366691,
"model": "claude-3-sonnet-20240229",
"object": "chat.completion",
"system_fingerprint": null,
"choices": [
{
"finish_reason": "stop",
"index": 0,
"message": {
"content": "Hello! As an AI language model, I don't have feelings, but I'm operating properly and ready to assist you with any questions or tasks you may have. How can I help you today?",
"role": "assistant",
"tool_calls": null,
"function_call": null
}
}
],
"usage": {
"completion_tokens": 43,
"prompt_tokens": 13,
"total_tokens": 56,
"completion_tokens_details": null,
"prompt_tokens_details": {
"audio_tokens": null,
"cached_tokens": 0
},
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0
}
}
```
Call any model supported by a provider, with `model=<provider_name>/<model_name>`. There might be provider-specific details here, so refer to [provider docs for more information](https://docs.litellm.ai/docs/providers)
## Async ([Docs](https://docs.litellm.ai/docs/completion/stream#async-completion))
```python
from litellm import acompletion
import asyncio
async def test_get_response():
user_message = "Hello, how are you?"
messages = [{"content": user_message, "role": "user"}]
response = await acompletion(model="openai/gpt-4o", messages=messages)
return response
response = asyncio.run(test_get_response())
print(response)
```
## Streaming ([Docs](https://docs.litellm.ai/docs/completion/stream))
liteLLM supports streaming the model response back, pass `stream=True` to get a streaming iterator in response.
Streaming is supported for all models (Bedrock, Huggingface, TogetherAI, Azure, OpenAI, etc.)
```python
from litellm import completion
response = completion(model="openai/gpt-4o", messages=messages, stream=True)
for part in response:
print(part.choices[0].delta.content or "")
# claude 2
response = completion('anthropic/claude-3-sonnet-20240229', messages, stream=True)
for part in response:
print(part)
```
### Response chunk (OpenAI Format)
```json
{
"id": "chatcmpl-2be06597-eb60-4c70-9ec5-8cd2ab1b4697",
"created": 1734366925,
"model": "claude-3-sonnet-20240229",
"object": "chat.completion.chunk",
"system_fingerprint": null,
"choices": [
{
"finish_reason": null,
"index": 0,
"delta": {
"content": "Hello",
"role": "assistant",
"function_call": null,
"tool_calls": null,
"audio": null
},
"logprobs": null
}
]
}
```
## Logging Observability ([Docs](https://docs.litellm.ai/docs/observability/callbacks))
LiteLLM exposes pre defined callbacks to send data to Lunary, MLflow, Langfuse, DynamoDB, s3 Buckets, Helicone, Promptlayer, Traceloop, Athina, Slack
```python
from litellm import completion
## set env variables for logging tools (when using MLflow, no API key set up is required)
os.environ["LUNARY_PUBLIC_KEY"] = "your-lunary-public-key"
os.environ["HELICONE_API_KEY"] = "your-helicone-auth-key"
os.environ["LANGFUSE_PUBLIC_KEY"] = ""
os.environ["LANGFUSE_SECRET_KEY"] = ""
os.environ["ATHINA_API_KEY"] = "your-athina-api-key"
os.environ["OPENAI_API_KEY"] = "your-openai-key"
# set callbacks
litellm.success_callback = ["lunary", "mlflow", "langfuse", "athina", "helicone"] # log input/output to lunary, langfuse, supabase, athina, helicone etc
#openai call
response = completion(model="openai/gpt-4o", messages=[{"role": "user", "content": "Hi 👋 - i'm openai"}])
```
# LiteLLM Proxy Server (LLM Gateway) - ([Docs](https://docs.litellm.ai/docs/simple_proxy))
Track spend + Load Balance across multiple projects
[Hosted Proxy (Preview)](https://docs.litellm.ai/docs/hosted)
The proxy provides:
1. [Hooks for auth](https://docs.litellm.ai/docs/proxy/virtual_keys#custom-auth)
2. [Hooks for logging](https://docs.litellm.ai/docs/proxy/logging#step-1---create-your-custom-litellm-callback-class)
3. [Cost tracking](https://docs.litellm.ai/docs/proxy/virtual_keys#tracking-spend)
4. [Rate Limiting](https://docs.litellm.ai/docs/proxy/users#set-rate-limits)
## 📖 Proxy Endpoints - [Swagger Docs](https://litellm-api.up.railway.app/)
## Quick Start Proxy - CLI
```shell
pip install 'litellm[proxy]'
```
### Step 1: Start litellm proxy
```shell
$ litellm --model huggingface/bigcode/starcoder
#INFO: Proxy running on http://0.0.0.0:4000
```
### Step 2: Make ChatCompletions Request to Proxy
> [!IMPORTANT]
> 💡 [Use LiteLLM Proxy with Langchain (Python, JS), OpenAI SDK (Python, JS) Anthropic SDK, Mistral SDK, LlamaIndex, Instructor, Curl](https://docs.litellm.ai/docs/proxy/user_keys)
```python
import openai # openai v1.0.0+
client = openai.OpenAI(api_key="anything",base_url="http://0.0.0.0:4000") # set proxy to base_url
# request sent to model set on litellm proxy, `litellm --model`
response = client.chat.completions.create(model="gpt-3.5-turbo", messages = [
{
"role": "user",
"content": "this is a test request, write a short poem"
}
])
print(response)
```
## Proxy Key Management ([Docs](https://docs.litellm.ai/docs/proxy/virtual_keys))
Connect the proxy with a Postgres DB to create proxy keys
```bash
# Get the code
git clone https://github.com/BerriAI/litellm
# Go to folder
cd litellm
# Add the master key - you can change this after setup
echo 'LITELLM_MASTER_KEY="sk-1234"' > .env
# Add the litellm salt key - you cannot change this after adding a model
# It is used to encrypt / decrypt your LLM API Key credentials
# We recommend - https://1password.com/password-generator/
# password generator to get a random hash for litellm salt key
echo 'LITELLM_SALT_KEY="sk-1234"' > .env
source .env
# Start
docker-compose up
```
UI on `/ui` on your proxy server
![ui_3](https://github.com/BerriAI/litellm/assets/29436595/47c97d5e-b9be-4839-b28c-43d7f4f10033)
Set budgets and rate limits across multiple projects
`POST /key/generate`
### Request
```shell
curl 'http://0.0.0.0:4000/key/generate' \
--header 'Authorization: Bearer sk-1234' \
--header 'Content-Type: application/json' \
--data-raw '{"models": ["gpt-3.5-turbo", "gpt-4", "claude-2"], "duration": "20m","metadata": {"user": "ishaan@berri.ai", "team": "core-infra"}}'
```
### Expected Response
```shell
{
"key": "sk-kdEXbIqZRwEeEiHwdg7sFA", # Bearer token
"expires": "2023-11-19T01:38:25.838000+00:00" # datetime object
}
```
## Supported Providers ([Docs](https://docs.litellm.ai/docs/providers))
| Provider | [Completion](https://docs.litellm.ai/docs/#basic-usage) | [Streaming](https://docs.litellm.ai/docs/completion/stream#streaming-responses) | [Async Completion](https://docs.litellm.ai/docs/completion/stream#async-completion) | [Async Streaming](https://docs.litellm.ai/docs/completion/stream#async-streaming) | [Async Embedding](https://docs.litellm.ai/docs/embedding/supported_embedding) | [Async Image Generation](https://docs.litellm.ai/docs/image_generation) |
|-------------------------------------------------------------------------------------|---------------------------------------------------------|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|-------------------------------------------------------------------------------|-------------------------------------------------------------------------|
| [openai](https://docs.litellm.ai/docs/providers/openai) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [azure](https://docs.litellm.ai/docs/providers/azure) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [AI/ML API](https://docs.litellm.ai/docs/providers/aiml) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [aws - sagemaker](https://docs.litellm.ai/docs/providers/aws_sagemaker) | ✅ | ✅ | ✅ | ✅ | ✅ | |
| [aws - bedrock](https://docs.litellm.ai/docs/providers/bedrock) | ✅ | ✅ | ✅ | ✅ | ✅ | |
| [google - vertex_ai](https://docs.litellm.ai/docs/providers/vertex) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [google - palm](https://docs.litellm.ai/docs/providers/palm) | ✅ | ✅ | ✅ | ✅ | | |
| [google AI Studio - gemini](https://docs.litellm.ai/docs/providers/gemini) | ✅ | ✅ | ✅ | ✅ | | |
| [mistral ai api](https://docs.litellm.ai/docs/providers/mistral) | ✅ | ✅ | ✅ | ✅ | ✅ | |
| [cloudflare AI Workers](https://docs.litellm.ai/docs/providers/cloudflare_workers) | ✅ | ✅ | ✅ | ✅ | | |
| [cohere](https://docs.litellm.ai/docs/providers/cohere) | ✅ | ✅ | ✅ | ✅ | ✅ | |
| [anthropic](https://docs.litellm.ai/docs/providers/anthropic) | ✅ | ✅ | ✅ | ✅ | | |
| [empower](https://docs.litellm.ai/docs/providers/empower) | ✅ | ✅ | ✅ | ✅ |
| [huggingface](https://docs.litellm.ai/docs/providers/huggingface) | ✅ | ✅ | ✅ | ✅ | ✅ | |
| [replicate](https://docs.litellm.ai/docs/providers/replicate) | ✅ | ✅ | ✅ | ✅ | | |
| [together_ai](https://docs.litellm.ai/docs/providers/togetherai) | ✅ | ✅ | ✅ | ✅ | | |
| [openrouter](https://docs.litellm.ai/docs/providers/openrouter) | ✅ | ✅ | ✅ | ✅ | | |
| [ai21](https://docs.litellm.ai/docs/providers/ai21) | ✅ | ✅ | ✅ | ✅ | | |
| [baseten](https://docs.litellm.ai/docs/providers/baseten) | ✅ | ✅ | ✅ | ✅ | | |
| [vllm](https://docs.litellm.ai/docs/providers/vllm) | ✅ | ✅ | ✅ | ✅ | | |
| [nlp_cloud](https://docs.litellm.ai/docs/providers/nlp_cloud) | ✅ | ✅ | ✅ | ✅ | | |
| [aleph alpha](https://docs.litellm.ai/docs/providers/aleph_alpha) | ✅ | ✅ | ✅ | ✅ | | |
| [petals](https://docs.litellm.ai/docs/providers/petals) | ✅ | ✅ | ✅ | ✅ | | |
| [ollama](https://docs.litellm.ai/docs/providers/ollama) | ✅ | ✅ | ✅ | ✅ | ✅ | |
| [deepinfra](https://docs.litellm.ai/docs/providers/deepinfra) | ✅ | ✅ | ✅ | ✅ | | |
| [perplexity-ai](https://docs.litellm.ai/docs/providers/perplexity) | ✅ | ✅ | ✅ | ✅ | | |
| [Groq AI](https://docs.litellm.ai/docs/providers/groq) | ✅ | ✅ | ✅ | ✅ | | |
| [Deepseek](https://docs.litellm.ai/docs/providers/deepseek) | ✅ | ✅ | ✅ | ✅ | | |
| [anyscale](https://docs.litellm.ai/docs/providers/anyscale) | ✅ | ✅ | ✅ | ✅ | | |
| [IBM - watsonx.ai](https://docs.litellm.ai/docs/providers/watsonx) | ✅ | ✅ | ✅ | ✅ | ✅ | |
| [voyage ai](https://docs.litellm.ai/docs/providers/voyage) | | | | | ✅ | |
| [xinference [Xorbits Inference]](https://docs.litellm.ai/docs/providers/xinference) | | | | | ✅ | |
| [FriendliAI](https://docs.litellm.ai/docs/providers/friendliai) | ✅ | ✅ | ✅ | ✅ | | |
| [Galadriel](https://docs.litellm.ai/docs/providers/galadriel) | ✅ | ✅ | ✅ | ✅ | | |
[**Read the Docs**](https://docs.litellm.ai/docs/)
##
## Contributing
Interested in contributing? Contributions to LiteLLM Python SDK, Proxy Server, and contributing LLM integrations are both accepted and highly encouraged! [See our Contribution Guide for more details](https://docs.litellm.ai/docs/extras/contributing_code)
Contributions and suggestions bug reports and fixes are welcome!
Please read the documentation before you do!
# Enterprise
For companies that need better security, user management and professional support
For new features, components, or extensions, please open an issue and discuss before sending a PR.
[Talk to founders](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat)
- Join the [Discord community](https://discord.gg/NGaa9RPCft)
This covers:
-**Features under the [LiteLLM Commercial License](https://docs.litellm.ai/docs/proxy/enterprise):**
-**Feature Prioritization**
-**Custom Integrations**
-**Professional Support - Dedicated discord + slack**
-**Custom SLAs**
-**Secure access with Single Sign-On**
## License
This project is licensed under the [MIT License](LICENSE.md).
##
# Code Quality / Linting
LiteLLM follows the [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html).
We run:
- Ruff for [formatting and linting checks](https://github.com/BerriAI/litellm/blob/e19bb55e3b4c6a858b6e364302ebbf6633a51de5/.circleci/config.yml#L320)
- Mypy + Pyright for typing [1](https://github.com/BerriAI/litellm/blob/e19bb55e3b4c6a858b6e364302ebbf6633a51de5/.circleci/config.yml#L90), [2](https://github.com/BerriAI/litellm/blob/e19bb55e3b4c6a858b6e364302ebbf6633a51de5/.pre-commit-config.yaml#L4)
- Black for [formatting](https://github.com/BerriAI/litellm/blob/e19bb55e3b4c6a858b6e364302ebbf6633a51de5/.circleci/config.yml#L79)
- isort for [import sorting](https://github.com/BerriAI/litellm/blob/e19bb55e3b4c6a858b6e364302ebbf6633a51de5/.pre-commit-config.yaml#L10)
If you have suggestions on how to improve the code quality feel free to open an issue or a PR.
# Support / talk with founders
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
# Why did we build this
- **Need for simplicity**: Our code started to get extremely complicated managing & translating calls between Azure, OpenAI and Cohere.
# Contributors
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
<a href="https://github.com/BerriAI/litellm/graphs/contributors">
<img src="https://contrib.rocks/image?repo=BerriAI/litellm" />
</a>
## Run in Developer mode
### Services
1. Setup .env file in root
2. Run dependant services `docker-compose up db prometheus`
### Backend
1. (In root) create virtual environment `python -m venv .venv`
2. Activate virtual environment `source .venv/bin/activate`
3. Install dependencies `pip install -e ".[all]"`
4. Start proxy backend `uvicorn litellm.proxy.proxy_server:app --host localhost --port 4000 --reload`
### Frontend
1. Navigate to `ui/litellm-dashboard`
2. Install dependencies `npm install`
3. Run `npm run dev` to start the dashboard
+146
View File
@@ -0,0 +1,146 @@
##########################
# Server configuration:
##########################
# The server will listen to localhost:3080 by default. You can change the target IP as you want.
# If you want to make this server available externally, for example to share the server with others
# or expose this from a Docker container, set host to 0.0.0.0 or your external IP interface.
# Tips: Setting host to 0.0.0.0 means listening on all interfaces. It's not a real IP.
# Use localhost:port rather than 0.0.0.0:port to access the server.
# Set Node env to development if running in dev mode.
HOST=localhost
PORT=3080
NODE_ENV=production
# Change this to proxy any API request.
# It's useful if your machine has difficulty calling the original API server.
# PROXY=
# Change this to your MongoDB URI if different. I recommend appending chatgpt-clone.
MONGO_URI=mongodb://127.0.0.1:27017/chatgpt-clone
##########################
# OpenAI Endpoint:
##########################
# Access key from OpenAI platform.
# Leave it blank to disable this feature.
OPENAI_KEY=
# Identify the available models, separated by commas *without spaces*.
# The first will be default.
# Leave it blank to use internal settings.
OPENAI_MODELS=gpt-3.5-turbo,gpt-3.5-turbo-0301,text-davinci-003,gpt-4
# Reverse proxy settings for OpenAI:
# https://github.com/waylaidwanderer/node-chatgpt-api#using-a-reverse-proxy
# OPENAI_REVERSE_PROXY=
##########################
# AZURE Endpoint:
##########################
# To use Azure with this project, set the following variables. These will be used to build the API URL.
# Chat completion:
# `https://{AZURE_OPENAI_API_INSTANCE_NAME}.openai.azure.com/openai/deployments/{AZURE_OPENAI_API_DEPLOYMENT_NAME}/chat/completions?api-version={AZURE_OPENAI_API_VERSION}`;
# You should also consider changing the `OPENAI_MODELS` variable above to the models available in your instance/deployment.
# Note: I've noticed that the Azure API is much faster than the OpenAI API, so the streaming looks almost instantaneous.
# AZURE_OPENAI_API_KEY=
# AZURE_OPENAI_API_INSTANCE_NAME=
# AZURE_OPENAI_API_DEPLOYMENT_NAME=
# AZURE_OPENAI_API_VERSION=
# AZURE_OPENAI_API_COMPLETIONS_DEPLOYMENT_NAME= # Optional, but may be used in future updates
# AZURE_OPENAI_API_EMBEDDINGS_DEPLOYMENT_NAME= # Optional, but may be used in future updates
##########################
# BingAI Endpoint:
##########################
# Also used for Sydney and jailbreak
# BingAI Tokens: the "_U" cookies value from bing.com
# Set to "user_provided" to allow the user to provide its token from the UI.
# Leave it blank to disable this endpoint.
BINGAI_TOKEN="user_provided"
# BingAI Host:
# Necessary for some people in different countries, e.g. China (https://cn.bing.com)
# Leave it blank to use default server.
# BINGAI_HOST=https://cn.bing.com
##########################
# ChatGPT Endpoint:
##########################
# ChatGPT Browser Client (free but use at your own risk)
# Access token from https://chat.openai.com/api/auth/session
# Exposes your access token to `CHATGPT_REVERSE_PROXY`
# Set to "user_provided" to allow the user to provide its token from the UI.
# Leave it blank to disable this endpoint
CHATGPT_TOKEN="user_provided"
# Identify the available models, separated by commas. The first will be default.
# Leave it blank to use internal settings.
CHATGPT_MODELS=text-davinci-002-render-sha,text-davinci-002-render-paid,gpt-4
# Reverse proxy settings for ChatGPT
# https://github.com/waylaidwanderer/node-chatgpt-api#using-a-reverse-proxy
# By default, the server will use the node-chatgpt-api recommended proxy (a third party server).
# CHATGPT_REVERSE_PROXY=
##########################
# Search:
##########################
# ENABLING SEARCH MESSAGES/CONVOS
# Requires the installation of the free self-hosted Meilisearch or a paid Remote Plan (Remote not tested)
# The easiest setup for this is through docker-compose, which takes care of it for you.
SEARCH=false
# REQUIRED FOR SEARCH: MeiliSearch Host, mainly for the API server to connect to the search server.
# Replace '0.0.0.0' with 'meilisearch' if serving MeiliSearch with docker-compose.
MEILI_HOST=http://0.0.0.0:7700
# REQUIRED FOR SEARCH: MeiliSearch HTTP Address, mainly for docker-compose to expose the search server.
# Replace '0.0.0.0' with 'meilisearch' if serving MeiliSearch with docker-compose.
MEILI_HTTP_ADDR=0.0.0.0:7700
# REQUIRED FOR SEARCH: In production env., a secure key is needed. You can generate your own.
# This master key must be at least 16 bytes, composed of valid UTF-8 characters.
# MeiliSearch will throw an error and refuse to launch if no master key is provided,
# or if it is under 16 bytes. MeiliSearch will suggest a secure autogenerated master key.
# Using docker, it seems recognized as production so use a secure key.
# This is a ready made secure key for docker-compose, you can replace it with your own.
MEILI_MASTER_KEY=DrhYf7zENyR6AlUCKmnz0eYASOQdl6zxH7s7MKFSfFCt
##########################
# User System:
##########################
# Google:
# Add your Google Client ID and Secret here, you must register an app with Google Cloud to get these values
# https://cloud.google.com/
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_CALLBACK_URL=/oauth/google/callback
#JWT:
JWT_SECRET_DEV=secret
# Add a secure secret for production if deploying to live domain.
JWT_SECRET_PROD=secret
# Set the expiration delay for the secure cookie with the JWT token
# Delay is in millisecond e.g. 7 days is 1000*60*60*24*7
SESSION_EXPIRY=1000 * 60 * 60 * 24 * 7
# Site URLs:
# Don't forget to set Node env to development in the Server configuration section above
# if you want to run in dev mode
CLIENT_URL_DEV=http://localhost:3090
SERVER_URL_DEV=http://localhost:3080
# Change these values to domain if deploying:
CLIENT_URL_PROD=http://localhost:3080
SERVER_URL_PROD=http://localhost:3080
+39
View File
@@ -0,0 +1,39 @@
module.exports = {
env: {
es2021: true,
node: true
},
extends: ['eslint:recommended'],
overrides: [],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
},
rules: {
indent: ['error', 2, { SwitchCase: 1 }],
'max-len': [
'error',
{
code: 150,
ignoreStrings: true,
ignoreTemplateLiterals: true,
ignoreComments: true
}
],
'linebreak-style': 0,
'arrow-parens': [2, 'as-needed', { requireForBlockBody: true }],
// 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],
'no-console': 'off',
'import/extensions': 'off',
'no-use-before-define': [
'error',
{
functions: false
}
],
'no-promise-executor-return': 'off',
'no-param-reassign': 'off',
'no-continue': 'off',
'no-restricted-syntax': 'off'
}
};
+22
View File
@@ -0,0 +1,22 @@
{
"arrowParens": "always",
"bracketSpacing": true,
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"singleAttributePerLine": true,
"bracketSameLine": false,
"jsxBracketSameLine": false,
"jsxSingleQuote": false,
"printWidth": 110,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"useTabs": false,
"vueIndentScriptAndStyle": false,
"parser": "babel"
}
+79
View File
@@ -0,0 +1,79 @@
require('dotenv').config();
const { KeyvFile } = require('keyv-file');
const askBing = async ({
text,
parentMessageId,
conversationId,
jailbreak,
jailbreakConversationId,
context,
systemMessage,
conversationSignature,
clientId,
invocationId,
toneStyle,
token,
onProgress
}) => {
const { BingAIClient } = await import('og-chatgpt-api');
const store = {
store: new KeyvFile({ filename: './data/cache.json' })
};
const bingAIClient = new BingAIClient({
// "_U" cookie from bing.com
userToken: process.env.BINGAI_TOKEN == 'user_provided' ? token : process.env.BINGAI_TOKEN ?? null,
// If the above doesn't work, provide all your cookies as a string instead
// cookies: '',
debug: false,
cache: store,
host: process.env.BINGAI_HOST || null,
proxy: process.env.PROXY || null
});
let options = {};
if (jailbreakConversationId == 'false') {
jailbreakConversationId = false;
}
if (jailbreak)
options = {
jailbreakConversationId: jailbreakConversationId || jailbreak,
context,
systemMessage,
parentMessageId,
toneStyle,
onProgress
};
else {
options = {
conversationId,
context,
systemMessage,
parentMessageId,
toneStyle,
onProgress
};
// don't give those parameters for new conversation
// for new conversation, conversationSignature always is null
if (conversationSignature) {
options.conversationSignature = conversationSignature;
options.clientId = clientId;
options.invocationId = invocationId;
}
}
console.log('bing options', options);
const res = await bingAIClient.sendMessage(text, options);
return res;
// for reference:
// https://github.com/waylaidwanderer/node-chatgpt-api/blob/main/demos/use-bing-client.js
};
module.exports = { askBing };
+47
View File
@@ -0,0 +1,47 @@
require('dotenv').config();
const { KeyvFile } = require('keyv-file');
const browserClient = async ({
text,
parentMessageId,
conversationId,
model,
token,
onProgress,
abortController,
userId
}) => {
const { ChatGPTBrowserClient } = await import('og-chatgpt-api');
const store = {
store: new KeyvFile({ filename: './data/cache.json' })
};
const clientOptions = {
// Warning: This will expose your access token to a third party. Consider the risks before using this.
reverseProxyUrl: process.env.CHATGPT_REVERSE_PROXY || 'https://ai.fakeopen.com/api/conversation',
// Access token from https://chat.openai.com/api/auth/session
accessToken: process.env.CHATGPT_TOKEN == 'user_provided' ? token : process.env.CHATGPT_TOKEN ?? null,
model: model,
debug: false,
proxy: process.env.PROXY || null,
user: userId
};
const client = new ChatGPTBrowserClient(clientOptions, store);
let options = { onProgress, abortController };
if (!!parentMessageId && !!conversationId) {
options = { ...options, parentMessageId, conversationId };
}
console.log('gptBrowser clientOptions', clientOptions);
if (parentMessageId === '00000000-0000-0000-0000-000000000000') {
delete options.conversationId;
}
const res = await client.sendMessage(text, options);
return res;
};
module.exports = { browserClient };
+66
View File
@@ -0,0 +1,66 @@
require('dotenv').config();
const { KeyvFile } = require('keyv-file');
const { genAzureEndpoint } = require('../../utils/genAzureEndpoints');
const askClient = async ({
text,
parentMessageId,
conversationId,
model,
chatGptLabel,
promptPrefix,
temperature,
top_p,
presence_penalty,
frequency_penalty,
onProgress,
abortController,
userId
}) => {
const { ChatGPTClient } = await import('@waylaidwanderer/chatgpt-api');
const store = {
store: new KeyvFile({ filename: './data/cache.json' })
};
const azure = process.env.AZURE_OPENAI_API_KEY ? true : false;
const clientOptions = {
reverseProxyUrl: process.env.OPENAI_REVERSE_PROXY || null,
azure,
modelOptions: {
model: model,
temperature,
top_p,
presence_penalty,
frequency_penalty
},
chatGptLabel,
promptPrefix,
proxy: process.env.PROXY || null,
debug: false
};
let apiKey = process.env.OPENAI_KEY;
if (azure) {
apiKey = process.env.AZURE_OPENAI_API_KEY;
clientOptions.reverseProxyUrl = genAzureEndpoint({
azureOpenAIApiInstanceName: process.env.AZURE_OPENAI_API_INSTANCE_NAME,
azureOpenAIApiDeploymentName: process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME,
azureOpenAIApiVersion: process.env.AZURE_OPENAI_API_VERSION
});
}
const client = new ChatGPTClient(apiKey, clientOptions, store);
const options = {
onProgress,
abortController,
...(parentMessageId && conversationId ? { parentMessageId, conversationId } : {})
};
const res = await client.sendMessage(text, { ...options, userId });
return res;
};
module.exports = { askClient };
+15
View File
@@ -0,0 +1,15 @@
const { askClient } = require('./clients/chatgpt-client');
const { browserClient } = require('./clients/chatgpt-browser');
const { askBing } = require('./clients/bingai');
const titleConvo = require('./titleConvo');
const getCitations = require('../lib/parse/getCitations');
const citeText = require('../lib/parse/citeText');
module.exports = {
askClient,
browserClient,
askBing,
titleConvo,
getCitations,
citeText
};
+78
View File
@@ -0,0 +1,78 @@
const { Configuration, OpenAIApi } = require('openai');
const _ = require('lodash');
const { genAzureEndpoint } = require('../utils/genAzureEndpoints');
const proxyEnvToAxiosProxy = (proxyString) => {
if (!proxyString) return null;
const regex = /^([^:]+):\/\/(?:([^:@]*):?([^:@]*)@)?([^:]+)(?::(\d+))?/;
const [, protocol, username, password, host, port] = proxyString.match(regex);
const proxyConfig = {
protocol,
host,
port: port ? parseInt(port) : undefined,
auth: username && password ? { username, password } : undefined
};
return proxyConfig;
};
const titleConvo = async ({ endpoint, text, response }) => {
let title = 'New Chat';
const ChatGPTClient = (await import('@waylaidwanderer/chatgpt-api')).default;
try {
const instructionsPayload = {
role: 'system',
content: `Detect user language and write in the same language an extremely concise title for this conversation, which you must accurately detect. Write in the detected language. Title in 5 Words or Less. No Punctuation or Quotation. All first letters of every word should be capitalized and complete only the title in User Language only.
||>User:
"${text}"
||>Response:
"${JSON.stringify(response?.text)}"
||>Title:`
};
const azure = process.env.AZURE_OPENAI_API_KEY ? true : false;
const options = {
azure,
reverseProxyUrl: process.env.OPENAI_REVERSE_PROXY || null,
proxy: process.env.PROXY || null
};
const titleGenClientOptions = JSON.parse(JSON.stringify(options));
titleGenClientOptions.modelOptions = {
model: 'gpt-3.5-turbo',
temperature: 0,
presence_penalty: 0,
frequency_penalty: 0
};
let apiKey = process.env.OPENAI_KEY;
if (azure) {
apiKey = process.env.AZURE_OPENAI_API_KEY;
titleGenClientOptions.reverseProxyUrl = genAzureEndpoint({
azureOpenAIApiInstanceName: process.env.AZURE_OPENAI_API_INSTANCE_NAME,
azureOpenAIApiDeploymentName: process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME,
azureOpenAIApiVersion: process.env.AZURE_OPENAI_API_VERSION
});
}
const titleGenClient = new ChatGPTClient(apiKey, titleGenClientOptions);
const result = await titleGenClient.getCompletion([instructionsPayload], null);
title = result.choices[0].message.content.replace(/\s+/g, ' ').replaceAll('"', '').trim();
} catch (e) {
console.error(e);
console.log('There was an issue generating title, see error above');
}
console.log('CONVERSATION TITLE', title);
return title;
};
const throttledTitleConvo = _.throttle(titleConvo, 1000);
module.exports = throttledTitleConvo;
+44
View File
@@ -0,0 +1,44 @@
require('dotenv').config();
const mongoose = require('mongoose');
const MONGO_URI = process.env.MONGO_URI;
if (!MONGO_URI) {
throw new Error('Please define the MONGO_URI environment variable inside .env.local');
}
/**
* Global is used here to maintain a cached connection across hot reloads
* in development. This prevents connections growing exponentially
* during API Route usage.
*/
let cached = global.mongoose;
if (!cached) {
cached = global.mongoose = { conn: null, promise: null };
}
async function connectDb() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
useNewUrlParser: true,
useUnifiedTopology: true,
bufferCommands: false
// bufferMaxEntries: 0,
// useFindAndModify: true,
// useCreateIndex: true
};
mongoose.set('strictQuery', true);
cached.promise = mongoose.connect(MONGO_URI, opts).then((mongoose) => {
return mongoose;
});
}
cached.conn = await cached.promise;
return cached.conn;
}
module.exports = connectDb;
+70
View File
@@ -0,0 +1,70 @@
const mongoose = require('mongoose');
const Conversation = mongoose.models.Conversation;
const Message = mongoose.models.Message;
const { MeiliSearch } = require('meilisearch');
let currentTimeout = null;
// eslint-disable-next-line no-unused-vars
async function indexSync(req, res, next) {
try {
if (!process.env.MEILI_HOST || !process.env.MEILI_MASTER_KEY || !process.env.SEARCH) {
throw new Error('Meilisearch not configured, search will be disabled.');
}
const client = new MeiliSearch({
host: process.env.MEILI_HOST,
apiKey: process.env.MEILI_MASTER_KEY
});
const { status } = await client.health();
// console.log(`Meilisearch: ${status}`);
const result = status === 'available' && !!process.env.SEARCH;
if (!result) {
throw new Error('Meilisearch not available');
}
const messageCount = await Message.countDocuments();
const convoCount = await Conversation.countDocuments();
const messages = await client.index('messages').getStats();
const convos = await client.index('convos').getStats();
const messagesIndexed = messages.numberOfDocuments;
const convosIndexed = convos.numberOfDocuments;
console.log(`There are ${messageCount} messages in the database, ${messagesIndexed} indexed`);
console.log(`There are ${convoCount} convos in the database, ${convosIndexed} indexed`);
if (messageCount !== messagesIndexed) {
console.log('Messages out of sync, indexing');
await Message.syncWithMeili();
}
if (convoCount !== convosIndexed) {
console.log('Convos out of sync, indexing');
await Conversation.syncWithMeili();
}
} catch (err) {
// console.log('in index sync');
if (err.message.includes('not found')) {
console.log('Creating indices...');
currentTimeout = setTimeout(async () => {
try {
await Message.syncWithMeili();
await Conversation.syncWithMeili();
} catch (err) {
console.error('Trouble creating indices, try restarting the server.');
}
}, 750);
} else {
console.error(err);
// res.status(500).json({ error: 'Server error' });
}
}
}
process.on('exit', () => {
console.log('Clearing sync timeouts before exiting...');
clearTimeout(currentTimeout);
});
module.exports = indexSync;
+118
View File
@@ -0,0 +1,118 @@
const mongoose = require('mongoose');
const { Conversation } = require('../../models/Conversation');
const { getMessages } = require('../../models/');
const migrateToStrictFollowParentMessageIdChain = async () => {
try {
const conversations = await Conversation.find({ endpoint: null, model: null }).exec();
if (!conversations || conversations.length === 0) return { noNeed: true };
console.log('Migration: To strict follow the parentMessageId chain.');
for (let convo of conversations) {
const messages = await getMessages({
conversationId: convo.conversationId,
messageId: { $exists: false }
});
let model;
let oldId;
const promises = [];
messages.forEach((message, i) => {
const msgObj = message.toObject();
const newId = msgObj.id;
if (i === 0) {
message.parentMessageId = '00000000-0000-0000-0000-000000000000';
} else {
message.parentMessageId = oldId;
}
oldId = newId;
message.messageId = newId;
if (message.sender.toLowerCase() !== 'user' && !model) {
model = message.sender.toLowerCase();
}
if (message.sender.toLowerCase() === 'user') {
message.isCreatedByUser = true;
}
promises.push(message.save());
});
await Promise.all(promises);
await Conversation.findOneAndUpdate(
{ conversationId: convo.conversationId },
{ model },
{ new: true }
).exec();
}
try {
await mongoose.connection.db.collection('messages').dropIndex('id_1');
} catch (error) {
console.log("[Migrate] Index doesn't exist or already dropped");
}
} catch (error) {
console.log(error);
return { message: '[Migrate] Error migrating conversations' };
}
};
const migrateToSupportBetterCustomization = async () => {
try {
const conversations = await Conversation.find({ endpoint: null }).exec();
if (!conversations || conversations.length === 0) return { noNeed: true };
console.log('Migration: To support better customization.');
const promises = [];
for (let convo of conversations) {
const originalModel = convo?.model;
if (originalModel === 'chatgpt') {
convo.endpoint = 'openAI';
convo.model = 'gpt-3.5-turbo';
} else if (originalModel === 'chatgptCustom') {
convo.endpoint = 'openAI';
convo.model = 'gpt-3.5-turbo';
} else if (originalModel === 'bingai') {
convo.endpoint = 'bingAI';
convo.model = null;
convo.jailbreak = false;
} else if (originalModel === 'sydney') {
convo.endpoint = 'bingAI';
convo.model = null;
convo.jailbreak = true;
} else if (originalModel === 'chatgptBrowser') {
convo.endpoint = 'chatGPTBrowser';
convo.model = 'text-davinci-002-render-sha';
convo.jailbreak = true;
} else {
convo.endpoint = 'openAI';
convo.model = 'gpt-3.5-turbo';
}
promises.push(convo.save());
}
await Promise.all(promises);
} catch (error) {
console.log(error);
return { message: '[Migrate] Error migrating conversations' };
}
};
async function migrateDb() {
let ret = [];
ret[0] = await migrateToStrictFollowParentMessageIdChain();
ret[1] = await migrateToSupportBetterCustomization();
const isMigrated = !!ret.find(element => !element?.noNeed);
if (!isMigrated) console.log('[Migrate] Nothing to migrate');
}
module.exports = migrateDb;
+31
View File
@@ -0,0 +1,31 @@
const citationRegex = /\[\^\d+?\^\]/g;
const citeText = (res, noLinks = false) => {
let result = res.text || res;
const citations = Array.from(new Set(result.match(citationRegex)));
if (citations?.length === 0) return result;
if (noLinks) {
citations.forEach((citation) => {
const digit = citation.match(/\d+?/g)[0];
// result = result.replaceAll(citation, `<sup>[${digit}](#) </sup>`);
result = result.replaceAll(citation, `<sup>[${digit}](#) </sup>`);
});
return result;
}
let sources = res.details.sourceAttributions;
if (sources?.length === 0) return result;
sources = sources.map((source) => source.seeMoreUrl);
citations.forEach((citation) => {
const digit = citation.match(/\d+?/g)[0];
result = result.replaceAll(citation, `<sup>[${digit}](${sources[digit - 1]}) </sup>`);
// result = result.replaceAll(citation, `<sup>[${digit}](${sources[digit - 1]}) </sup>`);
});
return result;
};
module.exports = citeText;
+14
View File
@@ -0,0 +1,14 @@
// const regex = / \[\d+\..*?\]\(.*?\)/g;
const regex = / \[.*?]\(.*?\)/g;
const getCitations = (res) => {
const adaptiveCards = res.details.adaptiveCards;
const textBlocks = adaptiveCards && adaptiveCards[0].body;
if (!textBlocks) return '';
let links = textBlocks[textBlocks.length - 1]?.text.match(regex);
if (links?.length === 0 || !links) return '';
links = links.map((link) => link.trim());
return links.join('\n');
};
module.exports = getCitations;
+29
View File
@@ -0,0 +1,29 @@
function mergeSort(arr, compareFn) {
if (arr.length <= 1) {
return arr;
}
const mid = Math.floor(arr.length / 2);
const leftArr = arr.slice(0, mid);
const rightArr = arr.slice(mid);
return merge(mergeSort(leftArr, compareFn), mergeSort(rightArr, compareFn), compareFn);
}
function merge(leftArr, rightArr, compareFn) {
const result = [];
let leftIndex = 0;
let rightIndex = 0;
while (leftIndex < leftArr.length && rightIndex < rightArr.length) {
if (compareFn(leftArr[leftIndex], rightArr[rightIndex]) < 0) {
result.push(leftArr[leftIndex++]);
} else {
result.push(rightArr[rightIndex++]);
}
}
return result.concat(leftArr.slice(leftIndex)).concat(rightArr.slice(rightIndex));
}
module.exports = mergeSort;
+15
View File
@@ -0,0 +1,15 @@
const cleanUpPrimaryKeyValue = (value) => {
// For Bing convoId handling
return value.replace(/--/g, '|');
};
function replaceSup(text) {
if (!text.includes('<sup>')) return text;
const replacedText = text.replace(/<sup>/g, '^').replace(/\s+<\/sup>/g, '^');
return replacedText;
}
module.exports = {
cleanUpPrimaryKeyValue,
replaceSup
};
+59
View File
@@ -0,0 +1,59 @@
const mergeSort = require('./mergeSort');
const { cleanUpPrimaryKeyValue } = require('./misc');
function reduceMessages(hits) {
const counts = {};
for (const hit of hits) {
if (!counts[hit.conversationId]) {
counts[hit.conversationId] = 1;
} else {
counts[hit.conversationId]++;
}
}
const result = [];
for (const [conversationId, count] of Object.entries(counts)) {
result.push({
conversationId,
count
});
}
return mergeSort(result, (a, b) => b.count - a.count);
}
function reduceHits(hits, titles = []) {
const counts = {};
const titleMap = {};
const convos = [...hits, ...titles];
for (const convo of convos) {
const currentId = cleanUpPrimaryKeyValue(convo.conversationId);
if (!counts[currentId]) {
counts[currentId] = 1;
} else {
counts[currentId]++;
}
if (convo.title) {
// titleMap[currentId] = convo._formatted.title;
titleMap[currentId] = convo.title;
}
}
const result = [];
for (const [conversationId, count] of Object.entries(counts)) {
result.push({
conversationId,
count,
title: titleMap[conversationId] ? titleMap[conversationId] : null
});
}
return mergeSort(result, (a, b) => b.count - a.count);
}
module.exports = { reduceMessages, reduceHits };
+5
View File
@@ -0,0 +1,5 @@
const passport = require('passport');
const requireJwtAuth = passport.authenticate('jwt', { session: false });
module.exports = requireJwtAuth;
+31
View File
@@ -0,0 +1,31 @@
const passport = require('passport');
const DebugControl = require('../utils/debug.js');
function log({ title, parameters }) {
DebugControl.log.functionName(title);
if (parameters) {
DebugControl.log.parameters(parameters);
}
}
const requireLocalAuth = (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
if (err) {
log({
title: '(requireLocalAuth) Error at passport.authenticate',
parameters: [{ name: 'error', value: err }]
});
return next(err);
}
if (!user) {
log({
title: '(requireLocalAuth) Error: No user',
});
return res.status(422).send(info);
}
req.user = user;
next();
})(req, res, next);
};
module.exports = requireLocalAuth;
+84
View File
@@ -0,0 +1,84 @@
const mongoose = require('mongoose');
const major = [0, 0];
const minor = [0, 0];
const patch = [0, 5];
const configSchema = mongoose.Schema(
{
tag: {
type: String,
required: true,
validate: {
validator: function (tag) {
const [part1, part2, part3] = tag.replace('v', '').split('.').map(Number);
// Check if all parts are numbers
if (isNaN(part1) || isNaN(part2) || isNaN(part3)) {
return false;
}
// Check if all parts are within their respective ranges
if (part1 < major[0] || part1 > major[1]) {
return false;
}
if (part2 < minor[0] || part2 > minor[1]) {
return false;
}
if (part3 < patch[0] || part3 > patch[1]) {
return false;
}
return true;
},
message: 'Invalid tag value'
}
},
searchEnabled: {
type: Boolean,
default: false
},
usersEnabled: {
type: Boolean,
default: false
},
startupCounts: {
type: Number,
default: 0
}
},
{ timestamps: true }
);
// Instance method
configSchema.methods.incrementCount = function () {
this.startupCounts += 1;
};
// Static methods
configSchema.statics.findByTag = async function (tag) {
return await this.findOne({ tag });
};
configSchema.statics.updateByTag = async function (tag, update) {
return await this.findOneAndUpdate({ tag }, update, { new: true });
};
const Config = mongoose.models.Config || mongoose.model('Config', configSchema);
module.exports = {
getConfigs: async (filter) => {
try {
return await Config.find(filter).exec();
} catch (error) {
console.error(error);
return { config: 'Error getting configs' };
}
},
deleteConfigs: async (filter) => {
try {
return await Config.deleteMany(filter).exec();
} catch (error) {
console.error(error);
return { config: 'Error deleting configs' };
}
}
};
+128
View File
@@ -0,0 +1,128 @@
// const { Conversation } = require('./plugins');
const Conversation = require('./schema/convoSchema');
const { getMessages, deleteMessages } = require('./Message');
const getConvo = async (user, conversationId) => {
try {
return await Conversation.findOne({ user, conversationId }).exec();
} catch (error) {
console.log(error);
return { message: 'Error getting single conversation' };
}
};
module.exports = {
Conversation,
saveConvo: async (user, { conversationId, newConversationId, ...convo }) => {
try {
const messages = await getMessages({ conversationId });
const update = { ...convo, messages, user };
if (newConversationId) {
update.conversationId = newConversationId;
}
return await Conversation.findOneAndUpdate({ conversationId: conversationId, user }, update, {
new: true,
upsert: true
}).exec();
} catch (error) {
console.log(error);
return { message: 'Error saving conversation' };
}
},
getConvosByPage: async (user, pageNumber = 1, pageSize = 12) => {
try {
const totalConvos = (await Conversation.countDocuments({ user })) || 1;
const totalPages = Math.ceil(totalConvos / pageSize);
const convos = await Conversation.find({ user })
.sort({ createdAt: -1, created: -1 })
.skip((pageNumber - 1) * pageSize)
.limit(pageSize)
.exec();
return { conversations: convos, pages: totalPages, pageNumber, pageSize };
} catch (error) {
console.log(error);
return { message: 'Error getting conversations' };
}
},
getConvosQueried: async (user, convoIds, pageNumber = 1, pageSize = 12) => {
try {
if (!convoIds || convoIds.length === 0) {
return { conversations: [], pages: 1, pageNumber, pageSize };
}
const cache = {};
const convoMap = {};
const promises = [];
// will handle a syncing solution soon
const deletedConvoIds = [];
convoIds.forEach((convo) =>
promises.push(
Conversation.findOne({
user,
conversationId: convo.conversationId
}).exec()
)
);
const results = (await Promise.all(promises)).filter((convo, i) => {
if (!convo) {
deletedConvoIds.push(convoIds[i].conversationId);
return false;
} else {
const page = Math.floor(i / pageSize) + 1;
if (!cache[page]) {
cache[page] = [];
}
cache[page].push(convo);
convoMap[convo.conversationId] = convo;
return true;
}
});
// const startIndex = (pageNumber - 1) * pageSize;
// const convos = results.slice(startIndex, startIndex + pageSize);
const totalPages = Math.ceil(results.length / pageSize);
cache.pages = totalPages;
cache.pageSize = pageSize;
return {
cache,
conversations: cache[pageNumber] || [],
pages: totalPages || 1,
pageNumber,
pageSize,
// will handle a syncing solution soon
filter: new Set(deletedConvoIds),
convoMap
};
} catch (error) {
console.log(error);
return { message: 'Error fetching conversations' };
}
},
getConvo,
/* chore: this method is not properly error handled */
getConvoTitle: async (user, conversationId) => {
try {
const convo = await getConvo(user, conversationId);
/* ChatGPT Browser was triggering error here due to convo being saved later */
if (convo && !convo.title) {
return null;
} else {
// TypeError: Cannot read properties of null (reading 'title')
return convo?.title || 'New Chat';
}
} catch (error) {
console.log(error);
return { message: 'Error getting conversation title' };
}
},
deleteConvos: async (user, filter) => {
let toRemove = await Conversation.find({ ...filter, user }).select('conversationId');
const ids = toRemove.map((instance) => instance.conversationId);
let deleteCount = await Conversation.deleteMany({ ...filter, user }).exec();
deleteCount.messages = await deleteMessages({ conversationId: { $in: ids } });
return deleteCount;
}
};
+86
View File
@@ -0,0 +1,86 @@
const Message = require('./schema/messageSchema');
module.exports = {
Message,
async saveMessage({
messageId,
newMessageId,
conversationId,
parentMessageId,
sender,
text,
isCreatedByUser = false,
error,
unfinished,
cancelled
}) {
try {
// may also need to update the conversation here
await Message.findOneAndUpdate(
{ messageId },
{
messageId: newMessageId || messageId,
conversationId,
parentMessageId,
sender,
text,
isCreatedByUser,
error,
unfinished,
cancelled
},
{ upsert: true, new: true }
);
return {
messageId,
conversationId,
parentMessageId,
sender,
text,
isCreatedByUser
};
} catch (err) {
console.error(`Error saving message: ${err}`);
throw new Error('Failed to save message.');
}
},
async deleteMessagesSince({ messageId, conversationId }) {
try {
const message = await Message.findOne({ messageId }).exec();
if (message) {
return await Message.find({ conversationId })
.deleteMany({ createdAt: { $gt: message.createdAt } })
.exec();
}
} catch (err) {
console.error(`Error deleting messages: ${err}`);
throw new Error('Failed to delete messages.');
}
},
async getMessages(filter) {
try {
return await Message.find(filter).sort({ createdAt: 1 }).exec();
} catch (err) {
console.error(`Error getting messages: ${err}`);
throw new Error('Failed to get messages.');
}
},
async deleteMessages(filter) {
try {
return await Message.deleteMany(filter).exec();
} catch (err) {
console.error(`Error deleting messages: ${err}`);
throw new Error('Failed to delete messages.');
}
}
};
+46
View File
@@ -0,0 +1,46 @@
const Preset = require('./schema/presetSchema');
const getPreset = async (user, presetId) => {
try {
return await Preset.findOne({ user, presetId }).exec();
} catch (error) {
console.log(error);
return { message: 'Error getting single preset' };
}
};
module.exports = {
Preset,
getPreset,
getPresets: async (user, filter) => {
try {
return await Preset.find({ ...filter, user }).exec();
} catch (error) {
console.log(error);
return { message: 'Error retriving presets' };
}
},
savePreset: async (user, { presetId, newPresetId, ...preset }) => {
try {
const update = { presetId, ...preset };
if (newPresetId) {
update.presetId = newPresetId;
}
return await Preset.findOneAndUpdate(
{ presetId, user },
{ $set: update },
{ new: true, upsert: true }
).exec();
} catch (error) {
console.log(error);
return { message: 'Error saving preset' };
}
},
deletePresets: async (user, filter) => {
let toRemove = await Preset.find({ ...filter, user }).select('presetId');
const ids = toRemove.map(instance => instance.presetId);
let deleteCount = await Preset.deleteMany({ ...filter, user }).exec();
return deleteCount;
}
};
+48
View File
@@ -0,0 +1,48 @@
const mongoose = require('mongoose');
const promptSchema = mongoose.Schema({
title: {
type: String,
required: true
},
prompt: {
type: String,
required: true
},
category: {
type: String,
},
}, { timestamps: true });
const Prompt = mongoose.models.Prompt || mongoose.model('Prompt', promptSchema);
module.exports = {
savePrompt: async ({ title, prompt }) => {
try {
await Prompt.create({
title,
prompt
});
return { title, prompt };
} catch (error) {
console.error(error);
return { prompt: 'Error saving prompt' };
}
},
getPrompts: async (filter) => {
try {
return await Prompt.find(filter).exec()
} catch (error) {
console.error(error);
return { prompt: 'Error getting prompts' };
}
},
deletePrompts: async (filter) => {
try {
return await Prompt.deleteMany(filter).exec()
} catch (error) {
console.error(error);
return { prompt: 'Error deleting prompts' };
}
}
}
+177
View File
@@ -0,0 +1,177 @@
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const Joi = require('joi');
const DebugControl = require('../utils/debug.js');
function log({ title, parameters }) {
DebugControl.log.functionName(title);
DebugControl.log.parameters(parameters);
}
const Session = mongoose.Schema({
refreshToken: {
type: String,
default: ''
}
});
const userSchema = mongoose.Schema(
{
name: {
type: String
},
username: {
type: String,
lowercase: true,
required: [true, "can't be blank"],
match: [/^[a-zA-Z0-9_]+$/, 'is invalid'],
index: true
},
email: {
type: String,
required: [true, "can't be blank"],
lowercase: true,
unique: true,
match: [/\S+@\S+\.\S+/, 'is invalid'],
index: true
},
emailVerified: {
type: Boolean,
required: true,
default: false
},
password: {
type: String,
trim: true,
minlength: 8,
maxlength: 60
},
avatar: {
type: String,
required: false
},
provider: {
type: String,
required: true,
default: 'local'
},
role: {
type: String,
default: 'USER'
},
googleId: {
type: String,
unique: true,
sparse: true
},
facebookId: {
type: String,
unique: true,
sparse: true
},
refreshToken: {
type: [Session]
}
},
{ timestamps: true }
);
//Remove refreshToken from the response
userSchema.set('toJSON', {
transform: function (doc, ret, options) {
delete ret.refreshToken;
return ret;
}
});
userSchema.methods.toJSON = function () {
return {
id: this._id,
provider: this.provider,
email: this.email,
name: this.name,
username: this.username,
avatar: this.avatar,
role: this.role,
emailVerified: this.emailVerified,
createdAt: this.createdAt,
updatedAt: this.updatedAt
};
};
const isProduction = process.env.NODE_ENV === 'production';
const secretOrKey = isProduction ? process.env.JWT_SECRET_PROD : process.env.JWT_SECRET_DEV;
const refreshSecret = isProduction
? process.env.REFRESH_TOKEN_SECRET_PROD
: process.env.REFRESH_TOKEN_SECRET_DEV;
userSchema.methods.generateToken = function () {
const token = jwt.sign(
{
id: this._id,
username: this.username,
provider: this.provider,
email: this.email
},
secretOrKey,
{ expiresIn: eval(process.env.SESSION_EXPIRY) }
);
return token;
};
userSchema.methods.generateRefreshToken = function () {
const refreshToken = jwt.sign(
{
id: this._id,
username: this.username,
provider: this.provider,
email: this.email
},
refreshSecret,
{ expiresIn: eval(process.env.REFRESH_TOKEN_EXPIRY) }
);
return refreshToken;
};
userSchema.methods.comparePassword = function (candidatePassword, callback) {
bcrypt.compare(candidatePassword, this.password, (err, isMatch) => {
if (err) return callback(err);
callback(null, isMatch);
});
};
module.exports.hashPassword = async (password) => {
const hashedPassword = await new Promise((resolve, reject) => {
bcrypt.hash(password, 10, function (err, hash) {
if (err) reject(err);
else resolve(hash);
});
});
return hashedPassword;
};
module.exports.validateUser = (user) => {
log({
title: 'Validate User',
parameters: [{ name: 'Validate User', value: user }]
});
const schema = {
avatar: Joi.any(),
name: Joi.string().min(2).max(80).required(),
username: Joi.string()
.min(2)
.max(80)
.regex(/^[a-zA-Z0-9_]+$/)
.required(),
password: Joi.string().min(8).max(60).allow('').allow(null)
};
return Joi.validate(user, schema);
};
const User = mongoose.model('User', userSchema);
module.exports = User;
+19
View File
@@ -0,0 +1,19 @@
const { getMessages, saveMessage, deleteMessagesSince, deleteMessages } = require('./Message');
const { getConvoTitle, getConvo, saveConvo } = require('./Conversation');
const { getPreset, getPresets, savePreset, deletePresets } = require('./Preset');
module.exports = {
getMessages,
saveMessage,
deleteMessagesSince,
deleteMessages,
getConvoTitle,
getConvo,
saveConvo,
getPreset,
getPresets,
savePreset,
deletePresets
};
+211
View File
@@ -0,0 +1,211 @@
const mongoose = require('mongoose');
const { MeiliSearch } = require('meilisearch');
const { cleanUpPrimaryKeyValue } = require('../../lib/utils/misc');
const _ = require('lodash');
const validateOptions = function (options) {
const requiredKeys = ['host', 'apiKey', 'indexName'];
requiredKeys.forEach((key) => {
if (!options[key]) throw new Error(`Missing mongoMeili Option: ${key}`);
});
};
const createMeiliMongooseModel = function ({ index, indexName, client, attributesToIndex }) {
// console.log('attributesToIndex', attributesToIndex);
const primaryKey = attributesToIndex[0];
// MeiliMongooseModel is of type Mongoose.Model
class MeiliMongooseModel {
// Clear Meili index
static async clearMeiliIndex() {
await index.delete();
// await index.deleteAllDocuments();
await this.collection.updateMany(
{ _meiliIndex: true },
{ $set: { _meiliIndex: false } }
);
}
static async resetIndex() {
await this.clearMeiliIndex();
await client.createIndex(indexName, { primaryKey });
}
// Clear Meili index
// Push a mongoDB collection to Meili index
static async syncWithMeili() {
await this.resetIndex();
// const docs = await this.find();
const docs = await this.find({ _meiliIndex: { $in: [null, false] } });
console.log('docs', docs.length);
await Promise.all(
docs.map(function (doc) {
return doc.addObjectToMeili();
})
);
}
// Set one or more settings of the meili index
static async setMeiliIndexSettings(settings) {
return await index.updateSettings(settings);
}
// Search the index
static async meiliSearch(q, params, populate) {
const data = await index.search(q, params);
// Populate hits with content from mongodb
if (populate) {
// Find objects into mongodb matching `objectID` from Meili search
const query = {};
// query[primaryKey] = { $in: _.map(data.hits, primaryKey) };
query[primaryKey] = _.map(data.hits, hit => cleanUpPrimaryKeyValue(hit[primaryKey]));
// console.log('query', query);
const hitsFromMongoose = await this.find(
query,
_.reduce(
this.schema.obj,
function (results, value, key) {
return { ...results, [key]: 1 };
},
{ _id: 1 }
),
);
// Add additional data from mongodb into Meili search hits
const populatedHits = data.hits.map(function (hit) {
const query = {};
query[primaryKey] = hit[primaryKey];
const originalHit = _.find(hitsFromMongoose, query);
return {
...(originalHit ? originalHit.toJSON() : {}),
...hit
};
});
data.hits = populatedHits;
}
return data;
}
// Push new document to Meili
async addObjectToMeili() {
const object = _.pick(this.toJSON(), attributesToIndex);
// NOTE: MeiliSearch does not allow | in primary key, so we replace it with - for Bing convoIds
// object.conversationId = object.conversationId.replace(/\|/g, '-');
if (object.conversationId && object.conversationId.includes('|')) {
object.conversationId = object.conversationId.replace(/\|/g, '--');
}
try {
// console.log('Adding document to Meili', object);
await index.addDocuments([object]);
} catch (error) {
// console.log('Error adding document to Meili');
// console.error(error);
}
await this.collection.updateMany({ _id: this._id }, { $set: { _meiliIndex: true } });
}
// Update an existing document in Meili
async updateObjectToMeili() {
const object = _.pick(this.toJSON(), attributesToIndex);
await index.updateDocuments([object]);
}
// Delete a document from Meili
async deleteObjectFromMeili() {
await index.deleteDocument(this._id);
}
// * schema.post('save')
postSaveHook() {
if (this._meiliIndex) {
this.updateObjectToMeili();
} else {
this.addObjectToMeili();
}
}
// * schema.post('update')
postUpdateHook() {
if (this._meiliIndex) {
this.updateObjectToMeili();
}
}
// * schema.post('remove')
postRemoveHook() {
if (this._meiliIndex) {
this.deleteObjectFromMeili();
}
}
}
return MeiliMongooseModel;
};
module.exports = function mongoMeili(schema, options) {
// Vaidate Options for mongoMeili
validateOptions(options);
// Add meiliIndex to schema
schema.add({
_meiliIndex: {
type: Boolean,
required: false,
select: false,
default: false
}
});
const { host, apiKey, indexName, primaryKey } = options;
// Setup MeiliSearch Client
const client = new MeiliSearch({ host, apiKey });
// Asynchronously create the index
client.createIndex(indexName, { primaryKey });
// Setup the index to search for this schema
const index = client.index(indexName);
const attributesToIndex = [
..._.reduce(
schema.obj,
function (results, value, key) {
return value.meiliIndex ? [...results, key] : results;
// }, []), '_id'];
},
[]
)
];
schema.loadClass(createMeiliMongooseModel({ index, indexName, client, attributesToIndex }));
// Register hooks
schema.post('save', function (doc) {
doc.postSaveHook();
});
schema.post('update', function (doc) {
doc.postUpdateHook();
});
schema.post('remove', function (doc) {
doc.postRemoveHook();
});
schema.post('deleteMany', function () {
// console.log('deleteMany hook', doc);
if (Object.prototype.hasOwnProperty.call(schema.obj, 'messages')) {
console.log('Syncing convos...');
mongoose.model('Conversation').syncWithMeili();
}
if (Object.prototype.hasOwnProperty.call(schema.obj, 'messageId')) {
console.log('Syncing messages...');
mongoose.model('Message').syncWithMeili();
}
});
schema.post('findOneAndUpdate', function (doc) {
doc.postSaveHook();
});
};
+62
View File
@@ -0,0 +1,62 @@
module.exports = {
// endpoint: [azureOpenAI, openAI, bingAI, chatGPTBrowser]
endpoint: {
type: String,
default: null,
required: true
},
// for azureOpenAI, openAI, chatGPTBrowser only
model: {
type: String,
default: null,
required: false
},
// for azureOpenAI, openAI only
chatGptLabel: {
type: String,
default: null,
required: false
},
promptPrefix: {
type: String,
default: null,
required: false
},
temperature: {
type: Number,
default: 1,
required: false
},
top_p: {
type: Number,
default: 1,
required: false
},
presence_penalty: {
type: Number,
default: 0,
required: false
},
frequency_penalty: {
type: Number,
default: 0,
required: false
},
// for bingai only
jailbreak: {
type: Boolean,
default: false
},
context: {
type: String,
default: null
},
systemMessage: {
type: String,
default: null
},
toneStyle: {
type: String,
default: null
}
};
+56
View File
@@ -0,0 +1,56 @@
const mongoose = require('mongoose');
const mongoMeili = require('../plugins/mongoMeili');
const conversationPreset = require('./conversationPreset');
const convoSchema = mongoose.Schema(
{
conversationId: {
type: String,
unique: true,
required: true,
index: true,
meiliIndex: true
},
title: {
type: String,
default: 'New Chat',
meiliIndex: true
},
user: {
type: String,
default: null
},
messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }],
...conversationPreset,
// for bingAI only
jailbreakConversationId: {
type: String,
default: null
},
conversationSignature: {
type: String,
default: null
},
clientId: {
type: String,
default: null
},
invocationId: {
type: Number,
default: 1
}
},
{ timestamps: true }
);
if (process.env.MEILI_HOST && process.env.MEILI_MASTER_KEY) {
convoSchema.plugin(mongoMeili, {
host: process.env.MEILI_HOST,
apiKey: process.env.MEILI_MASTER_KEY,
indexName: 'convos', // Will get created automatically if it doesn't exist already
primaryKey: 'conversationId'
});
}
const Conversation = mongoose.models.Conversation || mongoose.model('Conversation', convoSchema);
module.exports = Conversation;
+79
View File
@@ -0,0 +1,79 @@
const mongoose = require('mongoose');
const mongoMeili = require('../plugins/mongoMeili');
const messageSchema = mongoose.Schema(
{
messageId: {
type: String,
unique: true,
required: true,
index: true,
meiliIndex: true
},
conversationId: {
type: String,
required: true,
meiliIndex: true
},
conversationSignature: {
type: String
// required: true
},
clientId: {
type: String
},
invocationId: {
type: String
},
parentMessageId: {
type: String
// required: true
},
sender: {
type: String,
required: true,
meiliIndex: true
},
text: {
type: String,
required: true,
meiliIndex: true
},
isCreatedByUser: {
type: Boolean,
required: true,
default: false
},
unfinished: {
type: Boolean,
default: false
},
cancelled: {
type: Boolean,
default: false
},
error: {
type: Boolean,
default: false
},
_meiliIndex: {
type: Boolean,
required: false,
select: false,
default: false
}
},
{ timestamps: true }
);
if (process.env.MEILI_HOST && process.env.MEILI_MASTER_KEY) {
messageSchema.plugin(mongoMeili, {
host: process.env.MEILI_HOST,
apiKey: process.env.MEILI_MASTER_KEY,
indexName: 'messages',
primaryKey: 'messageId'
});
}
const Message = mongoose.models.Message || mongoose.model('Message', messageSchema);
module.exports = Message;
+27
View File
@@ -0,0 +1,27 @@
const mongoose = require('mongoose');
const conversationPreset = require('./conversationPreset');
const presetSchema = mongoose.Schema(
{
presetId: {
type: String,
unique: true,
required: true,
index: true
},
title: {
type: String,
default: 'New Chat',
meiliIndex: true
},
user: {
type: String,
default: null
},
...conversationPreset
},
{ timestamps: true }
);
const Preset = mongoose.models.Preset || mongoose.model('Preset', presetSchema);
module.exports = Preset;
+22
View File
@@ -0,0 +1,22 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const tokenSchema = new Schema({
userId: {
type: Schema.Types.ObjectId,
required: true,
ref: "user",
},
token: {
type: String,
required: true,
},
createdAt: {
type: Date,
required: true,
default: Date.now,
expires: 900,
},
});
module.exports = mongoose.model("Token", tokenSchema);
+6
View File
@@ -0,0 +1,6 @@
{
"ignore": [
"api/data/",
"data"
]
}
+10968
View File
File diff suppressed because it is too large Load Diff
+59
View File
@@ -0,0 +1,59 @@
{
"name": "chatgpt-clone",
"version": "0.4.2",
"description": "",
"main": "server/index.js",
"scripts": {
"start": "node server/index.js",
"server-dev": "npx nodemon server/index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/danny-avila/chatgpt-clone.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/danny-avila/chatgpt-clone/issues"
},
"homepage": "https://github.com/danny-avila/chatgpt-clone#readme",
"dependencies": {
"@dqbd/tiktoken": "^1.0.2",
"@keyv/mongo": "^2.1.8",
"@waylaidwanderer/chatgpt-api": "github:danny-avila/node-chatgpt-api",
"axios": "^1.3.4",
"bcrypt": "^5.1.0",
"bcryptjs": "^2.4.3",
"cookie": "^0.5.0",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"crypto": "^1.0.1",
"dotenv": "^16.0.3",
"eslint": "^8.36.0",
"express": "^4.18.2",
"handlebars": "^4.7.7",
"html": "^1.0.0",
"joi": "^14.3.1",
"jsonwebtoken": "^9.0.0",
"keyv": "^4.5.2",
"keyv-file": "^0.2.0",
"lodash": "^4.17.21",
"meilisearch": "^0.31.1",
"mongoose": "^6.9.0",
"nodemailer": "^6.9.1",
"og-chatgpt-api": "npm:@waylaidwanderer/chatgpt-api@^1.35.0",
"openai": "^3.1.0",
"passport": "^0.6.0",
"passport-facebook": "^3.0.0",
"passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"pino": "^8.12.1",
"sanitize": "^2.1.2"
},
"devDependencies": {
"nodemon": "^2.0.20",
"path": "^0.12.7"
}
}
+180
View File
@@ -0,0 +1,180 @@
const {
loginUser,
logoutUser,
registerUser,
requestPasswordReset,
resetPassword,
} = require("../services/auth.service");
const isProduction = process.env.NODE_ENV === 'production';
const loginController = async (req, res) => {
try {
const token = req.user.generateToken();
const user = await loginUser(req.user)
if(user) {
res.cookie('token', token, {
expires: new Date(Date.now() + eval(process.env.SESSION_EXPIRY)),
httpOnly: false,
secure: isProduction
});
res.status(200).send({ token, user });
}
else {
return res.status(400).json({ message: 'Invalid credentials' });
}
}
catch (err) {
console.log(err);
return res.status(500).json({ message: err.message });
}
};
const logoutController = async (req, res) => {
const { signedCookies = {} } = req;
const { refreshToken } = signedCookies;
try {
const logout = await logoutUser(req.user, refreshToken);
console.log(logout)
const { status, message } = logout;
if (status === 200) {
res.clearCookie('token');
res.clearCookie('refreshToken');
res.status(status).send({ message });
}
else {
res.status(status).send({ message });
}
}
catch (err) {
console.log(err);
return res.status(500).json({ message: err.message });
}
}
const registrationController = async (req, res) => {
try {
const response = await registerUser(req.body);
if (response.status === 200) {
const { status, user } = response;
const token = user.generateToken();
//send token for automatic login
res.cookie('token', token, {
expires: new Date(Date.now() + eval(process.env.SESSION_EXPIRY)),
httpOnly: false,
secure: isProduction
});
res.status(status).send({ user });
}
else {
const { status, message } = response;
res.status(status).send({ message });
}
}
catch (err) {
console.log(err);
return res.status(500).json({ message: err.message });
}
};
const getUserController = async (req, res) => {
return res.status(200).send(req.user);
};
const resetPasswordRequestController = async (req, res) => {
try {
const resetService = await requestPasswordReset(
req.body.email
);
if (resetService.link) {
return res.status(200).json(resetService);
}
else {
return res.status(400).json(resetService);
}
}
catch (e) {
console.log(e);
return res.status(400).json({ message: e.message });
}
};
const resetPasswordController = async (req, res) => {
try {
const resetPasswordService = await resetPassword(
req.body.userId,
req.body.token,
req.body.password
);
if(resetPasswordService instanceof Error) {
return res.status(400).json(resetPasswordService);
}
else {
return res.status(200).json(resetPasswordService);
}
}
catch (e) {
console.log(e);
return res.status(400).json({ message: e.message });
}
};
const refreshController = async (req, res, next) => {
const { signedCookies = {} } = req;
const { refreshToken } = signedCookies;
//TODO
// if (refreshToken) {
// try {
// const payload = jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET);
// const userId = payload._id;
// User.findOne({ _id: userId }).then(
// (user) => {
// if (user) {
// // Find the refresh token against the user record in database
// const tokenIndex = user.refreshToken.findIndex(item => item.refreshToken === refreshToken);
// if (tokenIndex === -1) {
// res.statusCode = 401;
// res.send('Unauthorized');
// } else {
// const token = req.user.generateToken();
// // If the refresh token exists, then create new one and replace it.
// const newRefreshToken = req.user.generateRefreshToken();
// user.refreshToken[tokenIndex] = { refreshToken: newRefreshToken };
// user.save((err) => {
// if (err) {
// res.statusCode = 500;
// res.send(err);
// } else {
// // setTokenCookie(res, newRefreshToken);
// const user = req.user.toJSON();
// res.status(200).send({ token, user });
// }
// });
// }
// } else {
// res.statusCode = 401;
// res.send('Unauthorized');
// }
// },
// err => next(err)
// );
// } catch (err) {
// res.statusCode = 401;
// res.send('Unauthorized');
// }
// } else {
// res.statusCode = 401;
// res.send('Unauthorized');
// }
};
module.exports = {
getUserController,
loginController,
logoutController,
refreshController,
registrationController,
resetPasswordRequestController,
resetPasswordController,
};
@@ -0,0 +1,33 @@
//handle duplicates
const handleDuplicateKeyError = (err, res) => {
const field = Object.keys(err.keyValue);
const code = 409;
const error = `An document with that ${field} already exists.`;
console.log('congrats you hit the duped keys error');
res.status(code).send({ messages: error, fields: field });
};
//handle validation errors
const handleValidationError = (err, res) => {
console.log('congrats you hit the validation middleware');
let errors = Object.values(err.errors).map(el => el.message);
let fields = Object.values(err.errors).map(el => el.path);
let code = 400;
if (errors.length > 1) {
const formattedErrors = errors.join(' ');
res.status(code).send({ messages: formattedErrors, fields: fields });
} else {
res.status(code).send({ messages: errors, fields: fields });
}
};
// eslint-disable-next-line no-unused-vars
module.exports = (err, req, res, next) => {
try {
console.log('congrats you hit the error middleware');
if (err.name === 'ValidationError') return (err = handleValidationError(err, res));
if (err.code && err.code == 11000) return (err = handleDuplicateKeyError(err, res));
} catch (err) {
res.status(500).send('An unknown error occurred.');
}
};
+81
View File
@@ -0,0 +1,81 @@
const express = require('express');
const connectDb = require('../lib/db/connectDb');
const migrateDb = require('../lib/db/migrateDb');
const indexSync = require('../lib/db/indexSync');
const path = require('path');
const cors = require('cors');
const routes = require('./routes');
const errorController = require('./controllers/error.controller');
const passport = require('passport');
const port = process.env.PORT || 3080;
const host = process.env.HOST || 'localhost';
const projectPath = path.join(__dirname, '..', '..', 'client');
(async () => {
await connectDb();
console.log('Connected to MongoDB');
await migrateDb();
await indexSync();
const app = express();
app.use(errorController);
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(projectPath, 'dist')));
app.set('trust proxy', 1); // trust first proxy
app.use(cors());
// OAUTH
app.use(passport.initialize());
require('../strategies/jwtStrategy');
require('../strategies/localStrategy');
if(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
require('../strategies/googleStrategy');
}
if(process.env.FACEBOOK_CLIENT_ID && process.env.FACEBOOK_CLIENT_SECRET) {
require('../strategies/facebookStrategy');
}
app.use('/oauth', routes.oauth)
// api endpoint
app.use('/api/auth', routes.auth);
app.use('/api/search', routes.search);
app.use('/api/ask', routes.ask);
app.use('/api/messages', routes.messages);
app.use('/api/convos', routes.convos);
app.use('/api/presets', routes.presets);
app.use('/api/prompts', routes.prompts);
app.use('/api/tokenizer', routes.tokenizer);
app.use('/api/endpoints', routes.endpoints);
// static files
app.get('/*', function (req, res) {
res.sendFile(path.join(projectPath, 'dist', 'index.html'));
});
app.listen(port, host, () => {
if (host == '0.0.0.0')
console.log(
`Server listening on all interface at port ${port}. Use http://localhost:${port} to access it`
);
else console.log(`Server listening at http://${host == '0.0.0.0' ? 'localhost' : host}:${port}`);
});
})();
let messageCount = 0;
process.on('uncaughtException', (err) => {
if (!err.message.includes('fetch failed')) {
console.error('There was an uncaught error:', err.message);
}
if (err.message.includes('fetch failed')) {
if (messageCount === 0) {
console.error('Meilisearch error, search will be disabled');
messageCount++;
}
} else {
process.exit(1);
}
});
+64
View File
@@ -0,0 +1,64 @@
const Keyv = require('keyv');
const { KeyvFile } = require('keyv-file');
const addToCache = async ({ endpoint, endpointOption, userMessage, responseMessage }) => {
try {
const conversationsCache = new Keyv({
store: new KeyvFile({ filename: './data/cache.json' }),
namespace: 'chatgpt' // should be 'bing' for bing/sydney
});
const {
conversationId,
messageId: userMessageId,
parentMessageId: userParentMessageId,
text: userText
} = userMessage;
const {
messageId: responseMessageId,
parentMessageId: responseParentMessageId,
text: responseText
} = responseMessage;
let conversation = await conversationsCache.get(conversationId);
// used to generate a title for the conversation if none exists
// let isNewConversation = false;
if (!conversation) {
conversation = {
messages: [],
createdAt: Date.now()
};
// isNewConversation = true;
}
const roles = (options) => {
if (endpoint === 'openAI') {
return options?.chatGptLabel || 'ChatGPT';
} else if (endpoint === 'bingAI') {
return options?.jailbreak ? 'Sydney' : 'BingAI';
}
};
let _userMessage = {
id: userMessageId,
parentMessageId: userParentMessageId,
role: 'User',
message: userText
};
let _responseMessage = {
id: responseMessageId,
parentMessageId: responseParentMessageId,
role: roles(endpointOption),
message: responseText
};
conversation.messages.push(_userMessage, _responseMessage);
await conversationsCache.set(conversationId, conversation);
} catch (error) {
console.error('Trouble adding to cache', error);
}
};
module.exports = addToCache;
+255
View File
@@ -0,0 +1,255 @@
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
const { titleConvo, askBing } = require('../../../app');
const { saveMessage, getConvoTitle, saveConvo, getConvo } = require('../../../models');
const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
const requireJwtAuth = require('../../../middleware/requireJwtAuth');
router.post('/', requireJwtAuth, async (req, res) => {
const {
endpoint,
text,
messageId,
overrideParentMessageId = null,
parentMessageId,
conversationId: oldConversationId
} = req.body;
if (text.length === 0) return handleError(res, { text: 'Prompt empty or too short' });
if (endpoint !== 'bingAI') return handleError(res, { text: 'Illegal request' });
// build user message
const conversationId = oldConversationId || crypto.randomUUID();
const isNewConversation = !oldConversationId;
const userMessageId = messageId;
const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000';
let userMessage = {
messageId: userMessageId,
sender: 'User',
text,
parentMessageId: userParentMessageId,
conversationId,
isCreatedByUser: true
};
// build endpoint option
let endpointOption = {};
if (req.body?.jailbreak)
endpointOption = {
jailbreak: req.body?.jailbreak ?? false,
jailbreakConversationId: req.body?.jailbreakConversationId ?? null,
systemMessage: req.body?.systemMessage ?? null,
context: req.body?.context ?? null,
toneStyle: req.body?.toneStyle ?? 'fast',
token: req.body?.token ?? null
};
else
endpointOption = {
jailbreak: req.body?.jailbreak ?? false,
systemMessage: req.body?.systemMessage ?? null,
context: req.body?.context ?? null,
conversationSignature: req.body?.conversationSignature ?? null,
clientId: req.body?.clientId ?? null,
invocationId: req.body?.invocationId ?? null,
toneStyle: req.body?.toneStyle ?? 'fast',
token: req.body?.token ?? null
};
console.log('ask log', {
userMessage,
endpointOption,
conversationId
});
if (!overrideParentMessageId) {
await saveMessage(userMessage);
await saveConvo(req.user.id, {
...userMessage,
...endpointOption,
conversationId,
endpoint
});
}
// eslint-disable-next-line no-use-before-define
return await ask({
isNewConversation,
userMessage,
endpointOption,
conversationId,
preSendRequest: true,
overrideParentMessageId,
req,
res
});
});
const ask = async ({
isNewConversation,
userMessage,
endpointOption,
conversationId,
preSendRequest = true,
overrideParentMessageId = null,
req,
res
}) => {
let { text, parentMessageId: userParentMessageId, messageId: userMessageId } = userMessage;
let responseMessageId = crypto.randomUUID();
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
'Access-Control-Allow-Origin': '*',
'X-Accel-Buffering': 'no'
});
if (preSendRequest) sendMessage(res, { message: userMessage, created: true });
try {
let lastSavedTimestamp = 0;
const { onProgress: progressCallback, getPartialText } = createOnProgress({
onProgress: ({ text }) => {
const currentTimestamp = Date.now();
if (currentTimestamp - lastSavedTimestamp > 500) {
lastSavedTimestamp = currentTimestamp;
saveMessage({
messageId: responseMessageId,
sender: endpointOption?.jailbreak ? 'Sydney' : 'BingAI',
conversationId,
parentMessageId: overrideParentMessageId || userMessageId,
text: text,
unfinished: true,
cancelled: false,
error: false
});
}
}
});
const abortController = new AbortController();
let response = await askBing({
text,
parentMessageId: userParentMessageId,
conversationId,
...endpointOption,
onProgress: progressCallback.call(null, {
res,
text,
parentMessageId: overrideParentMessageId || userMessageId
}),
abortController
});
console.log('BING RESPONSE', response);
const newConversationId = endpointOption?.jailbreak
? response.jailbreakConversationId
: response.conversationId || conversationId;
const newUserMassageId = response.parentMessageId || response.details.requestId || userMessageId;
const newResponseMessageId = response.messageId || response.details.messageId;
// STEP1 generate response message
response.text = response.response || response.details.spokenText || '**Bing refused to answer.**';
let responseMessage = {
conversationId: newConversationId,
messageId: responseMessageId,
newMessageId: newResponseMessageId,
parentMessageId: overrideParentMessageId || newUserMassageId,
sender: endpointOption?.jailbreak ? 'Sydney' : 'BingAI',
text: await handleText(response, true),
suggestions:
response.details.suggestedResponses && response.details.suggestedResponses.map((s) => s.text),
unfinished: false,
cancelled: false,
error: false
};
await saveMessage(responseMessage);
responseMessage.messageId = newResponseMessageId;
// STEP2 update the convosation.
// First update conversationId if needed
// Note!
// Bing API will not use our conversationId at the first time,
// so change the placeholder conversationId to the real one.
// Attition: the api will also create new conversationId while using invalid userMessage.parentMessageId,
// but in this situation, don't change the conversationId, but create new convo.
let conversationUpdate = { conversationId: newConversationId, endpoint: 'bingAI' };
if (conversationId != newConversationId)
if (isNewConversation) {
// change the conversationId to new one
conversationUpdate = {
...conversationUpdate,
conversationId: conversationId,
newConversationId: newConversationId
};
} else {
// create new conversation
conversationUpdate = {
...conversationUpdate,
...endpointOption
};
}
if (endpointOption?.jailbreak) {
conversationUpdate.jailbreak = true;
conversationUpdate.jailbreakConversationId = response.jailbreakConversationId;
} else {
conversationUpdate.jailbreak = false;
conversationUpdate.conversationSignature = response.conversationSignature;
conversationUpdate.clientId = response.clientId;
conversationUpdate.invocationId = response.invocationId;
}
await saveConvo(req.user.id, conversationUpdate);
conversationId = newConversationId;
// STEP3 update the user message
userMessage.conversationId = newConversationId;
userMessage.messageId = newUserMassageId;
// If response has parentMessageId, the fake userMessage.messageId should be updated to the real one.
if (!overrideParentMessageId)
await saveMessage({ ...userMessage, messageId: userMessageId, newMessageId: newUserMassageId });
userMessageId = newUserMassageId;
sendMessage(res, {
title: await getConvoTitle(req.user.id, conversationId),
final: true,
conversation: await getConvo(req.user.id, conversationId),
requestMessage: userMessage,
responseMessage: responseMessage
});
res.end();
if (userParentMessageId == '00000000-0000-0000-0000-000000000000') {
const title = await titleConvo({ endpoint: endpointOption?.endpoint, text, response: responseMessage });
await saveConvo(req.user.id, {
conversationId: conversationId,
title
});
}
} catch (error) {
console.log(error);
const errorMessage = {
messageId: responseMessageId,
sender: endpointOption?.jailbreak ? 'Sydney' : 'BingAI',
conversationId,
parentMessageId: overrideParentMessageId || userMessageId,
unfinished: false,
cancelled: false,
error: true,
text: error.message
};
await saveMessage(errorMessage);
handleError(res, errorMessage);
}
};
module.exports = router;
+219
View File
@@ -0,0 +1,219 @@
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
const { getChatGPTBrowserModels } = require('../endpoints');
const { browserClient } = require('../../../app/');
const { saveMessage, getConvoTitle, saveConvo, getConvo } = require('../../../models');
const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
const requireJwtAuth = require('../../../middleware/requireJwtAuth');
router.post('/', requireJwtAuth, async (req, res) => {
const {
endpoint,
text,
overrideParentMessageId = null,
parentMessageId,
conversationId: oldConversationId
} = req.body;
if (text.length === 0) return handleError(res, { text: 'Prompt empty or too short' });
if (endpoint !== 'chatGPTBrowser') return handleError(res, { text: 'Illegal request' });
// build user message
const conversationId = oldConversationId || crypto.randomUUID();
const isNewConversation = !oldConversationId;
const userMessageId = crypto.randomUUID();
const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000';
const userMessage = {
messageId: userMessageId,
sender: 'User',
text,
parentMessageId: userParentMessageId,
conversationId,
isCreatedByUser: true
};
// build endpoint option
const endpointOption = {
model: req.body?.model ?? 'text-davinci-002-render-sha',
token: req.body?.token ?? null
};
const availableModels = getChatGPTBrowserModels();
if (availableModels.find((model) => model === endpointOption.model) === undefined)
return handleError(res, { text: 'Illegal request: model' });
console.log('ask log', {
userMessage,
endpointOption,
conversationId
});
if (!overrideParentMessageId) {
await saveMessage(userMessage);
await saveConvo(req.user.id, {
...userMessage,
...endpointOption,
conversationId,
endpoint
});
}
// eslint-disable-next-line no-use-before-define
return await ask({
isNewConversation,
userMessage,
endpointOption,
conversationId,
preSendRequest: true,
overrideParentMessageId,
req,
res
});
});
const ask = async ({
isNewConversation,
userMessage,
endpointOption,
conversationId,
preSendRequest = true,
overrideParentMessageId = null,
req,
res
}) => {
let { text, parentMessageId: userParentMessageId, messageId: userMessageId } = userMessage;
const userId = req.user.id;
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
'Access-Control-Allow-Origin': '*',
'X-Accel-Buffering': 'no'
});
if (preSendRequest) sendMessage(res, { message: userMessage, created: true });
let responseMessageId = crypto.randomUUID();
try {
let lastSavedTimestamp = 0;
const { onProgress: progressCallback, getPartialText } = createOnProgress({
onProgress: ({ text }) => {
const currentTimestamp = Date.now();
if (currentTimestamp - lastSavedTimestamp > 500) {
lastSavedTimestamp = currentTimestamp;
saveMessage({
messageId: responseMessageId,
sender: endpointOption?.jailbreak ? 'Sydney' : 'BingAI',
conversationId,
parentMessageId: overrideParentMessageId || userMessageId,
text: text,
unfinished: true,
cancelled: false,
error: false
});
}
}
});
const abortController = new AbortController();
let response = await browserClient({
text,
parentMessageId: userParentMessageId,
conversationId,
...endpointOption,
onProgress: progressCallback.call(null, { res, text }),
abortController,
userId
});
console.log('CLIENT RESPONSE', response);
const newConversationId = response.conversationId || conversationId;
const newUserMassageId = response.parentMessageId || userMessageId;
const newResponseMessageId = response.messageId;
// STEP1 generate response message
response.text = response.response || '**ChatGPT refused to answer.**';
let responseMessage = {
conversationId: newConversationId,
messageId: responseMessageId,
newMessageId: newResponseMessageId,
parentMessageId: overrideParentMessageId || newUserMassageId,
text: await handleText(response),
sender: endpointOption?.chatGptLabel || 'ChatGPT',
unfinished: false,
cancelled: false,
error: false
};
await saveMessage(responseMessage);
responseMessage.messageId = newResponseMessageId;
// STEP2 update the conversation
// First update conversationId if needed
let conversationUpdate = { conversationId: newConversationId, endpoint: 'chatGPTBrowser' };
if (conversationId != newConversationId)
if (isNewConversation) {
// change the conversationId to new one
conversationUpdate = {
...conversationUpdate,
conversationId: conversationId,
newConversationId: newConversationId
};
} else {
// create new conversation
conversationUpdate = {
...conversationUpdate,
...endpointOption
};
}
await saveConvo(req.user.id, conversationUpdate);
conversationId = newConversationId;
// STEP3 update the user message
userMessage.conversationId = newConversationId;
userMessage.messageId = newUserMassageId;
// If response has parentMessageId, the fake userMessage.messageId should be updated to the real one.
if (!overrideParentMessageId)
await saveMessage({ ...userMessage, messageId: userMessageId, newMessageId: newUserMassageId });
userMessageId = newUserMassageId;
sendMessage(res, {
title: await getConvoTitle(req.user.id, conversationId),
final: true,
conversation: await getConvo(req.user.id, conversationId),
requestMessage: userMessage,
responseMessage: responseMessage
});
res.end();
if (userParentMessageId == '00000000-0000-0000-0000-000000000000') {
// const title = await titleConvo({ endpoint: endpointOption?.endpoint, text, response: responseMessage });
const title = await response.details.title;
await saveConvo(req.user.id, {
conversationId: conversationId,
title
});
}
} catch (error) {
const errorMessage = {
messageId: responseMessageId,
sender: 'ChatGPT',
conversationId,
parentMessageId: overrideParentMessageId || userMessageId,
unfinished: false,
cancelled: false,
error: true,
text: error.message
};
await saveMessage(errorMessage);
handleError(res, errorMessage);
}
};
module.exports = router;
+275
View File
@@ -0,0 +1,275 @@
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
const addToCache = require('./addToCache');
const { getOpenAIModels } = require('../endpoints');
const { titleConvo, askClient } = require('../../../app/');
const { saveMessage, getConvoTitle, saveConvo, getConvo } = require('../../../models');
const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
const requireJwtAuth = require('../../../middleware/requireJwtAuth');
const abortControllers = new Map();
router.post('/abort', requireJwtAuth, async (req, res) => {
const { abortKey } = req.body;
console.log(`req.body`, req.body);
if (!abortControllers.has(abortKey)) {
return res.status(404).send('Request not found');
}
const { abortController } = abortControllers.get(abortKey);
abortControllers.delete(abortKey);
const ret = await abortController.abortAsk();
console.log('Aborted request', abortKey);
console.log('Aborted message:', ret);
res.send(JSON.stringify(ret));
});
router.post('/', requireJwtAuth, async (req, res) => {
const {
endpoint,
text,
overrideParentMessageId = null,
parentMessageId,
conversationId: oldConversationId
} = req.body;
if (text.length === 0) return handleError(res, { text: 'Prompt empty or too short' });
if (endpoint !== 'openAI') return handleError(res, { text: 'Illegal request' });
// build user message
const conversationId = oldConversationId || crypto.randomUUID();
const isNewConversation = !oldConversationId;
const userMessageId = crypto.randomUUID();
const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000';
const userMessage = {
messageId: userMessageId,
sender: 'User',
text,
parentMessageId: userParentMessageId,
conversationId,
isCreatedByUser: true
};
// build endpoint option
const endpointOption = {
model: req.body?.model ?? 'gpt-3.5-turbo',
chatGptLabel: req.body?.chatGptLabel ?? null,
promptPrefix: req.body?.promptPrefix ?? null,
temperature: req.body?.temperature ?? 1,
top_p: req.body?.top_p ?? 1,
presence_penalty: req.body?.presence_penalty ?? 0,
frequency_penalty: req.body?.frequency_penalty ?? 0
};
const availableModels = getOpenAIModels();
if (availableModels.find(model => model === endpointOption.model) === undefined)
return handleError(res, { text: 'Illegal request: model' });
console.log('ask log', {
userMessage,
endpointOption,
conversationId
});
if (!overrideParentMessageId) {
await saveMessage(userMessage);
await saveConvo(req.user.id, {
...userMessage,
...endpointOption,
conversationId,
endpoint
});
}
// eslint-disable-next-line no-use-before-define
return await ask({
isNewConversation,
userMessage,
endpointOption,
conversationId,
preSendRequest: true,
overrideParentMessageId,
req,
res
});
});
const ask = async ({
isNewConversation,
userMessage,
endpointOption,
conversationId,
preSendRequest = true,
overrideParentMessageId = null,
req,
res
}) => {
let { text, parentMessageId: userParentMessageId, messageId: userMessageId } = userMessage;
const userId = req.user.id;
let responseMessageId = crypto.randomUUID();
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
'Access-Control-Allow-Origin': '*',
'X-Accel-Buffering': 'no'
});
if (preSendRequest) sendMessage(res, { message: userMessage, created: true });
try {
let lastSavedTimestamp = 0;
const { onProgress: progressCallback, getPartialText } = createOnProgress({
onProgress: ({ text }) => {
const currentTimestamp = Date.now();
if (currentTimestamp - lastSavedTimestamp > 500) {
lastSavedTimestamp = currentTimestamp;
saveMessage({
messageId: responseMessageId,
sender: endpointOption?.chatGptLabel || 'ChatGPT',
conversationId,
parentMessageId: overrideParentMessageId || userMessageId,
text: text,
unfinished: true,
cancelled: false,
error: false
});
}
}
});
let abortController = new AbortController();
abortController.abortAsk = async function () {
this.abort();
const responseMessage = {
messageId: responseMessageId,
sender: endpointOption?.chatGptLabel || 'ChatGPT',
conversationId,
parentMessageId: overrideParentMessageId || userMessageId,
text: getPartialText(),
unfinished: false,
cancelled: true,
error: false
};
saveMessage(responseMessage);
await addToCache({ endpoint: 'openAI', endpointOption, userMessage, responseMessage });
return {
title: await getConvoTitle(req.user.id, conversationId),
final: true,
conversation: await getConvo(req.user.id, conversationId),
requestMessage: userMessage,
responseMessage: responseMessage
};
};
const abortKey = conversationId;
abortControllers.set(abortKey, { abortController, ...endpointOption });
let response = await askClient({
text,
parentMessageId: userParentMessageId,
conversationId,
...endpointOption,
onProgress: progressCallback.call(null, {
res,
text,
parentMessageId: overrideParentMessageId || userMessageId
}),
abortController,
userId
});
abortControllers.delete(abortKey);
console.log('CLIENT RESPONSE', response);
const newConversationId = response.conversationId || conversationId;
const newUserMassageId = response.parentMessageId || userMessageId;
const newResponseMessageId = response.messageId;
// STEP1 generate response message
response.text = response.response || '**ChatGPT refused to answer.**';
let responseMessage = {
conversationId: newConversationId,
messageId: responseMessageId,
newMessageId: newResponseMessageId,
parentMessageId: overrideParentMessageId || newUserMassageId,
text: await handleText(response),
sender: endpointOption?.chatGptLabel || 'ChatGPT',
unfinished: false,
cancelled: false,
error: false
};
await saveMessage(responseMessage);
responseMessage.messageId = newResponseMessageId;
// STEP2 update the conversation
let conversationUpdate = { conversationId: newConversationId, endpoint: 'openAI' };
if (conversationId != newConversationId)
if (isNewConversation) {
// change the conversationId to new one
conversationUpdate = {
...conversationUpdate,
conversationId: conversationId,
newConversationId: newConversationId
};
} else {
// create new conversation
conversationUpdate = {
...conversationUpdate,
...endpointOption
};
}
await saveConvo(req.user.id, conversationUpdate);
conversationId = newConversationId;
// STEP3 update the user message
userMessage.conversationId = newConversationId;
userMessage.messageId = newUserMassageId;
// If response has parentMessageId, the fake userMessage.messageId should be updated to the real one.
if (!overrideParentMessageId)
await saveMessage({ ...userMessage, messageId: userMessageId, newMessageId: newUserMassageId });
userMessageId = newUserMassageId;
sendMessage(res, {
title: await getConvoTitle(req.user.id, conversationId),
final: true,
conversation: await getConvo(req.user.id, conversationId),
requestMessage: userMessage,
responseMessage: responseMessage
});
res.end();
if (userParentMessageId == '00000000-0000-0000-0000-000000000000') {
const title = await titleConvo({ endpoint: endpointOption?.endpoint, text, response: responseMessage });
await saveConvo(req.user.id, {
conversationId: conversationId,
title
});
}
} catch (error) {
console.error(error);
const errorMessage = {
messageId: responseMessageId,
sender: endpointOption?.chatGptLabel || 'ChatGPT',
conversationId,
parentMessageId: overrideParentMessageId || userMessageId,
unfinished: false,
cancelled: false,
error: true,
text: error.message
};
await saveMessage(errorMessage);
handleError(res, errorMessage);
}
};
module.exports = router;
+102
View File
@@ -0,0 +1,102 @@
const _ = require('lodash');
const citationRegex = /\[\^\d+?\^]/g;
const backtick = /(?<!`)[`](?!`)/g;
// const singleBacktick = /(?<!`)[`](?!`)/;
const cursorDefault = '<span className="result-streaming">█</span>';
const { getCitations, citeText } = require('../../../app');
const handleError = (res, message) => {
res.write(`event: error\ndata: ${JSON.stringify(message)}\n\n`);
res.end();
};
const sendMessage = (res, message) => {
if (message.length === 0) {
return;
}
res.write(`event: message\ndata: ${JSON.stringify(message)}\n\n`);
};
const createOnProgress = ({ onProgress: _onProgress }) => {
let i = 0;
let code = '';
let tokens = '';
let precode = '';
let blockCount = 0;
let codeBlock = false;
let cursor = cursorDefault;
const progressCallback = async (partial, { res, text, bing = false, ...rest }) => {
let chunk = partial === text ? '' : partial;
tokens += chunk;
precode += chunk;
tokens = tokens.replaceAll('[DONE]', '');
if (codeBlock) {
code += chunk;
}
if (precode.includes('```') && codeBlock) {
codeBlock = false;
cursor = cursorDefault;
precode = precode.replace(/```/g, '');
code = '';
}
if (precode.includes('```') && code === '') {
precode = precode.replace(/```/g, '');
codeBlock = true;
blockCount++;
cursor = blockCount > 1 ? '█\n\n```' : '█\n\n';
}
const backticks = precode.match(backtick);
if (backticks && !codeBlock && cursor === cursorDefault) {
precode = precode.replace(backtick, '');
cursor = '█';
}
if (tokens.match(/^\n/)) {
tokens = tokens.replace(/^\n/, '');
}
if (bing) {
tokens = citeText(tokens, true);
}
sendMessage(res, { text: tokens + cursor, message: true, initial: i === 0, ...rest });
_onProgress && _onProgress({ text: tokens, message: true, initial: i === 0, ...rest });
i++;
};
const onProgress = (opts) => {
return _.partialRight(progressCallback, opts);
};
const getPartialText = () => {
return tokens;
};
return { onProgress, getPartialText };
};
const handleText = async (response, bing = false) => {
let { text } = response;
// text = await detectCode(text);
response.text = text;
if (bing) {
// const hasCitations = response.response.match(citationRegex)?.length > 0;
const links = getCitations(response);
if (response.text.match(citationRegex)?.length > 0) {
text = citeText(response);
}
text += links?.length > 0 ? `\n<small>${links}</small>` : '';
}
return text;
};
module.exports = { handleError, sendMessage, createOnProgress, handleText };
+13
View File
@@ -0,0 +1,13 @@
const express = require('express');
const router = express.Router();
// const askAzureOpenAI = require('./askAzureOpenAI';)
const askOpenAI = require('./askOpenAI');
const askBingAI = require('./askBingAI');
const askChatGPTBrowser = require('./askChatGPTBrowser');
// router.use('/azureOpenAI', askAzureOpenAI);
router.use('/openAI', askOpenAI);
router.use('/bingAI', askBingAI);
router.use('/chatGPTBrowser', askChatGPTBrowser);
module.exports = router;
+25
View File
@@ -0,0 +1,25 @@
const express = require('express');
const {
resetPasswordRequestController,
resetPasswordController,
getUserController,
loginController,
logoutController,
refreshController,
registrationController,
} = require('../controllers/auth.controller');
const requireJwtAuth = require('../../middleware/requireJwtAuth');
const requireLocalAuth = require('../../middleware/requireLocalAuth');
const router = express.Router();
//Local
router.get('/user', requireJwtAuth, getUserController);
router.post('/logout', requireJwtAuth, logoutController);
router.post('/login', requireLocalAuth, loginController);
router.post('/refresh', requireJwtAuth, refreshController);
router.post('/register', registrationController);
router.post('/requestPasswordReset', resetPasswordRequestController);
router.post('/resetPassword', resetPasswordController);
module.exports = router;
+54
View File
@@ -0,0 +1,54 @@
const express = require('express');
const router = express.Router();
const { getConvo, saveConvo } = require('../../models');
const { getConvosByPage, deleteConvos } = require('../../models/Conversation');
const requireJwtAuth = require('../../middleware/requireJwtAuth');
router.get('/', requireJwtAuth, async (req, res) => {
const pageNumber = req.query.pageNumber || 1;
res.status(200).send(await getConvosByPage(req.user.id, pageNumber));
});
router.get('/:conversationId', requireJwtAuth, async (req, res) => {
const { conversationId } = req.params;
const convo = await getConvo(req.user.id, conversationId);
if (convo) res.status(200).send(convo.toObject());
else res.status(404).end();
});
router.post('/clear', requireJwtAuth, async (req, res) => {
let filter = {};
const { conversationId, source } = req.body.arg;
if (conversationId) {
filter = { conversationId };
}
console.log('source:', source);
if (source === 'button' && !conversationId) {
return res.status(200).send('No conversationId provided');
}
try {
const dbResponse = await deleteConvos(req.user.id, filter);
res.status(201).send(dbResponse);
} catch (error) {
console.error(error);
res.status(500).send(error);
}
});
router.post('/update', requireJwtAuth, async (req, res) => {
const update = req.body.arg;
try {
const dbResponse = await saveConvo(req.user.id, update);
res.status(201).send(dbResponse);
} catch (error) {
console.error(error);
res.status(500).send(error);
}
});
module.exports = router;
+34
View File
@@ -0,0 +1,34 @@
const express = require('express');
const router = express.Router();
const getOpenAIModels = () => {
let models = ['gpt-4', 'text-davinci-003', 'gpt-3.5-turbo', 'gpt-3.5-turbo-0301'];
if (process.env.OPENAI_MODELS) models = String(process.env.OPENAI_MODELS).split(',');
return models;
};
const getChatGPTBrowserModels = () => {
let models = ['text-davinci-002-render-sha', 'text-davinci-002-render-paid', 'gpt-4'];
if (process.env.CHATGPT_MODELS) models = String(process.env.CHATGPT_MODELS).split(',');
return models;
};
router.get('/', function (req, res) {
const azureOpenAI = !!process.env.AZURE_OPENAI_KEY;
const openAI = process.env.OPENAI_KEY || process.env.AZURE_OPENAI_API_KEY ? { availableModels: getOpenAIModels() } : false;
const bingAI = process.env.BINGAI_TOKEN
? { userProvide: process.env.BINGAI_TOKEN == 'user_provided' }
: false;
const chatGPTBrowser = process.env.CHATGPT_TOKEN
? {
userProvide: process.env.CHATGPT_TOKEN == 'user_provided',
availableModels: getChatGPTBrowserModels()
}
: false;
res.send(JSON.stringify({ azureOpenAI, openAI, bingAI, chatGPTBrowser }));
});
module.exports = { router, getOpenAIModels, getChatGPTBrowserModels };
+23
View File
@@ -0,0 +1,23 @@
const ask = require('./ask');
const messages = require('./messages');
const convos = require('./convos');
const presets = require('./presets');
const prompts = require('./prompts');
const search = require('./search');
const tokenizer = require('./tokenizer');
const auth = require('./auth');
const oauth = require('./oauth');
const { router: endpoints } = require('./endpoints');
module.exports = {
search,
ask,
messages,
convos,
presets,
prompts,
auth,
oauth,
tokenizer,
endpoints,
};
+11
View File
@@ -0,0 +1,11 @@
const express = require('express');
const router = express.Router();
const { getMessages } = require('../../models/Message');
const requireJwtAuth = require('../../middleware/requireJwtAuth');
router.get('/:conversationId', requireJwtAuth, async (req, res) => {
const { conversationId } = req.params;
res.status(200).send(await getMessages({ conversationId }));
});
module.exports = router;
+64
View File
@@ -0,0 +1,64 @@
const passport = require('passport');
const express = require('express');
const router = express.Router();
const isProduction = process.env.NODE_ENV === 'production';
const clientUrl = isProduction ? process.env.CLIENT_URL_PROD : process.env.CLIENT_URL_DEV;
// Social
router.get(
'/google',
passport.authenticate('google', {
scope: ['openid', 'profile', 'email'],
session: false
})
);
router.get(
'/google/callback',
passport.authenticate('google', {
failureRedirect: `${clientUrl}/login`,
failureMessage: true,
session: false,
scope: ['openid', 'profile', 'email']
}),
(req, res) => {
const token = req.user.generateToken();
res.cookie('token', token, {
expires: new Date(Date.now() + eval(process.env.SESSION_EXPIRY)),
httpOnly: false,
secure: isProduction
});
res.redirect(clientUrl);
}
);
router.get(
'/facebook',
passport.authenticate('facebook', {
scope: ['public_profile', 'email'],
session: false
})
);
router.get(
'/facebook/callback',
passport.authenticate('facebook', {
failureRedirect: `${clientUrl}/login`,
failureMessage: true,
session: false,
scope: ['public_profile', 'email']
}),
(req, res) => {
const token = req.user.generateToken();
res.cookie('token', token, {
expires: new Date(Date.now() + eval(process.env.SESSION_EXPIRY)),
httpOnly: false,
secure: isProduction
});
res.redirect(clientUrl);
}
);
module.exports = router;
+54
View File
@@ -0,0 +1,54 @@
const express = require('express');
const router = express.Router();
const { getPresets, savePreset, deletePresets } = require('../../models');
const crypto = require('crypto');
const requireJwtAuth = require('../../middleware/requireJwtAuth');
router.get('/', requireJwtAuth, async (req, res) => {
const presets = (await getPresets(req.user.id)).map((preset) => {
return preset.toObject();
});
res.status(200).send(presets);
});
router.post('/', requireJwtAuth, async (req, res) => {
const update = req.body || {};
update.presetId = update?.presetId || crypto.randomUUID();
try {
await savePreset(req.user.id, update);
const presets = (await getPresets(req.user.id)).map((preset) => {
return preset.toObject();
});
res.status(201).send(presets);
} catch (error) {
console.error(error);
res.status(500).send(error);
}
});
router.post('/delete', requireJwtAuth, async (req, res) => {
let filter = {};
const { presetId } = req.body.arg || {};
if (presetId) filter = { presetId };
console.log('delete preset filter', filter);
try {
await deletePresets(req.user.id, filter);
const presets = (await getPresets(req.user.id)).map(preset => preset.toObject());
// console.log('delete preset response', presets);
res.status(201).send(presets);
// res.status(201).send(dbResponse);
} catch (error) {
console.error(error);
res.status(500).send(error);
}
});
module.exports = router;
+14
View File
@@ -0,0 +1,14 @@
const express = require('express');
const router = express.Router();
const { savePrompt, getPrompts, deletePrompts } = require('../../models/Prompt');
router.get('/', async (req, res) => {
let filter = {};
// const { search } = req.body.arg;
// if (!!search) {
// filter = { conversationId };
// }
res.status(200).send(await getPrompts(filter));
});
module.exports = router;
+125
View File
@@ -0,0 +1,125 @@
const express = require('express');
const router = express.Router();
const { MeiliSearch } = require('meilisearch');
const { Message } = require('../../models/Message');
const { Conversation, getConvosQueried } = require('../../models/Conversation');
const { reduceHits } = require('../../lib/utils/reduceHits');
const { cleanUpPrimaryKeyValue } = require('../../lib/utils/misc');
const requireJwtAuth = require('../../middleware/requireJwtAuth');
const cache = new Map();
router.get('/sync', async function (req, res) {
await Message.syncWithMeili();
await Conversation.syncWithMeili();
res.send('synced');
});
router.get('/', requireJwtAuth, async function (req, res) {
try {
let user = req.user.id;
user = user ?? null;
const { q } = req.query;
const pageNumber = req.query.pageNumber || 1;
const key = `${user || ''}${q}`;
if (cache.has(key)) {
console.log('cache hit', key);
const cached = cache.get(key);
const { pages, pageSize, messages } = cached;
res.status(200).send({ conversations: cached[pageNumber], pages, pageNumber, pageSize, messages });
return;
} else {
cache.clear();
}
// const message = await Message.meiliSearch(q);
const messages = (
await Message.meiliSearch(
q,
{
attributesToHighlight: ['text'],
highlightPreTag: '**',
highlightPostTag: '**'
},
true
)
).hits.map(message => {
const { _formatted, ...rest } = message;
return {
...rest,
searchResult: true,
text: _formatted.text
};
});
const titles = (await Conversation.meiliSearch(q)).hits;
const sortedHits = reduceHits(messages, titles);
// debugging:
// console.log('user:', user, 'message hits:', messages.length, 'convo hits:', titles.length);
// console.log('sorted hits:', sortedHits.length);
const result = await getConvosQueried(user, sortedHits, pageNumber);
const activeMessages = [];
for (let i = 0; i < messages.length; i++) {
let message = messages[i];
if (message.conversationId.includes('--')) {
message.conversationId = cleanUpPrimaryKeyValue(message.conversationId);
}
if (result.convoMap[message.conversationId] && !message.error) {
const convo = result.convoMap[message.conversationId];
const { title, chatGptLabel, model } = convo;
message = { ...message, ...{ title, chatGptLabel, model } };
activeMessages.push(message);
}
}
result.messages = activeMessages;
if (result.cache) {
result.cache.messages = activeMessages;
cache.set(key, result.cache);
delete result.cache;
}
delete result.convoMap;
// for debugging
// console.log(result, messages.length);
res.status(200).send(result);
} catch (error) {
console.log(error);
res.status(500).send({ message: 'Error searching' });
}
});
router.get('/clear', async function (req, res) {
await Message.resetIndex();
res.send('cleared');
});
router.get('/test', async function (req, res) {
const { q } = req.query;
const messages = (await Message.meiliSearch(q, { attributesToHighlight: ['text'] }, true)).hits.map(
message => {
const { _formatted, ...rest } = message;
return { ...rest, searchResult: true, text: _formatted.text };
}
);
res.send(messages);
});
router.get('/enable', async function (req, res) {
let result = false;
try {
const client = new MeiliSearch({
host: process.env.MEILI_HOST,
apiKey: process.env.MEILI_MASTER_KEY
});
const { status } = await client.health();
// console.log(`Meilisearch: ${status}`);
result = status === 'available' && !!process.env.SEARCH;
return res.send(result);
} catch (error) {
// console.error(error);
return res.send(false);
}
});
module.exports = router;
+26
View File
@@ -0,0 +1,26 @@
const express = require('express');
const router = express.Router();
const { Tiktoken } = require('@dqbd/tiktoken/lite');
const { load } = require('@dqbd/tiktoken/load');
const registry = require('@dqbd/tiktoken/registry.json');
const models = require('@dqbd/tiktoken/model_to_encoding.json');
const requireJwtAuth = require('../../middleware/requireJwtAuth');
router.post('/', requireJwtAuth, async (req, res) => {
try {
const { arg } = req.body;
// console.log('context:', arg, req.body);
// console.log(typeof req.body === 'object' ? { ...req.body, ...req.query } : req.query);
const model = await load(registry[models['gpt-3.5-turbo']]);
const encoder = new Tiktoken(model.bpe_ranks, model.special_tokens, model.pat_str);
const tokens = encoder.encode(arg.text);
encoder.free();
res.send({ count: tokens.length });
} catch (e) {
console.error(e);
res.status(500).send(e.message);
}
});
module.exports = router;
+197
View File
@@ -0,0 +1,197 @@
const User = require('../../models/User');
const Token = require('../../models/schema/tokenSchema');
const sendEmail = require('../../utils/sendEmail');
const crypto = require('crypto');
const bcrypt = require('bcrypt');
const DebugControl = require('../../utils/debug.js');
const Joi = require('joi');
const { registerSchema } = require('../../strategies/validators');
const migrateDataToFirstUser = require('../../utils/migrateDataToFirstUser');
function log({ title, parameters }) {
DebugControl.log.functionName(title);
DebugControl.log.parameters(parameters);
}
const isProduction = process.env.NODE_ENV === 'production';
const clientUrl = isProduction ? process.env.CLIENT_URL_PROD : process.env.CLIENT_URL_DEV;
const loginUser = async (user) => {
// const refreshToken = req.user.generateRefreshToken();
const dbUser = await User.findById(user._id);
//todo: save refresh token
return dbUser;
};
const logoutUser = async (user, refreshToken) => {
User.findById(user._id).then((user) => {
const tokenIndex = user.refreshToken.findIndex(item => item.refreshToken === refreshToken);
if (tokenIndex !== -1) {
user.refreshToken.id(user.refreshToken[tokenIndex]._id).remove();
}
user.save((err) => {
if (err) {
return { status: 500, message: err.message };
} else {
//res.clearCookie('refreshToken', COOKIE_OPTIONS);
// removeTokenCookie(res);
return { status: 200, message: 'Logout successful' };
}
});
});
return { status: 200, message: 'Logout successful' };
};
const registerUser = async (user) => {
let response = {};
const { error } = Joi.validate(user, registerSchema);
if (error) {
log({
title: 'Route: register - Joi Validation Error',
parameters: [
{ name: 'Request params:', value: user },
{ name: 'Validation error:', value: error.details }
]
});
response = { status: 422, message: error.details[0].message };
return response;
}
const { email, password, name, username } = user;
try {
const existingUser = await User.findOne({ email });
if (existingUser) {
log({
title: 'Register User - Email in use',
parameters: [
{ name: 'Request params:', value: user },
{ name: 'Existing user:', value: existingUser }
]
});
response = { status: 422, message: 'Email is in use' };
return response;
}
//determine if this is the first registered user (not counting anonymous_user)
const isFirstRegisteredUser = await User.countDocuments({}) === 0;
try {
const newUser = await new User({
provider: 'local',
email,
password,
username,
name,
avatar: null,
role: isFirstRegisteredUser ? 'ADMIN' : 'USER',
});
// todo: implement refresh token
// const refreshToken = newUser.generateRefreshToken();
// newUser.refreshToken.push({ refreshToken });
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (errh, hash) => {
if (err) {
console.log(err);
}
// set pasword to hash
newUser.password = hash;
newUser.save();
});
});
console.log('newUser', newUser)
if (isFirstRegisteredUser) {
migrateDataToFirstUser(newUser);
// console.log(migrate);
}
response = { status: 200, user: newUser };
return response;
} catch (err) {
response = { status: 500, message: err.message };
return response;
}
} catch (err) {
response = { status: 500, message: err.message };
return response;
}
};
const requestPasswordReset = async (email) => {
const user = await User.findOne({ email });
if (!user) {
return new Error('Email does not exist');
}
let token = await Token.findOne({ userId: user._id });
if (token) await token.deleteOne();
let resetToken = crypto.randomBytes(32).toString('hex');
const hash = await bcrypt.hash(resetToken, 10);
await new Token({
userId: user._id,
token: hash,
createdAt: Date.now()
}).save();
const link = `${clientUrl}/reset-password?token=${resetToken}&userId=${user._id}`;
sendEmail(
user.email,
'Password Reset Request',
{
name: user.name,
link: link
},
'./template/requestResetPassword.handlebars'
);
return { link };
};
const resetPassword = async (userId, token, password) => {
let passwordResetToken = await Token.findOne({ userId });
if (!passwordResetToken) {
return new Error('Invalid or expired password reset token');
}
const isValid = await bcrypt.compare(token, passwordResetToken.token);
if (!isValid) {
return new Error('Invalid or expired password reset token');
}
const hash = await bcrypt.hash(password, 10);
await User.updateOne({ _id: userId }, { $set: { password: hash } }, { new: true });
const user = await User.findById({ _id: userId });
sendEmail(
user.email,
'Password Reset Successfnodeully',
{
name: user.name
},
'./template/resetPassword.handlebars'
);
await passwordResetToken.deleteOne();
return { message: 'Password reset was successful' };
};
module.exports = {
// signup,
registerUser,
loginUser,
logoutUser,
requestPasswordReset,
resetPassword,
};
+60
View File
@@ -0,0 +1,60 @@
const passport = require('passport');
const FacebookStrategy = require('passport-facebook').Strategy;
const User = require('../models/User');
const serverUrl =
process.env.NODE_ENV === 'production' ? process.env.SERVER_URL_PROD : process.env.SERVER_URL_DEV;
// facebook strategy
const facebookLogin = new FacebookStrategy(
{
clientID: process.env.FACEBOOK_APP_ID,
clientSecret: process.env.FACEBOOK_SECRET,
callbackURL: `${serverUrl}${process.env.FACEBOOK_CALLBACK_URL}`,
proxy: true,
// profileFields: [
// 'id',
// 'email',
// 'gender',
// 'profileUrl',
// 'displayName',
// 'locale',
// 'name',
// 'timezone',
// 'updated_time',
// 'verified',
// 'picture.type(large)'
// ]
},
async (accessToken, refreshToken, profile, done) => {
console.log('facebookLogin => profile', profile);
try {
const oldUser = await User.findOne({ email: profile.emails[0].value });
if (oldUser) {
console.log('FACEBOOK LOGIN => found user', oldUser);
return done(null, oldUser);
}
} catch (err) {
console.log(err);
}
// register user
try {
const newUser = await new User({
provider: 'facebook',
facebookId: profile.id,
username: profile.name.givenName + profile.name.familyName,
email: profile.emails[0].value,
name: profile.displayName,
avatar: profile.photos[0].value
}).save();
done(null, newUser);
} catch (err) {
console.log(err);
}
}
);
passport.use(facebookLogin);
+44
View File
@@ -0,0 +1,44 @@
const passport = require('passport');
const { Strategy: GoogleStrategy } = require('passport-google-oauth20');
const User = require('../models/User');
const serverUrl =
process.env.NODE_ENV === 'production' ? process.env.SERVER_URL_PROD : process.env.SERVER_URL_DEV;
// google strategy
const googleLogin = new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: `${serverUrl}${process.env.GOOGLE_CALLBACK_URL}`,
proxy: true
},
async (accessToken, refreshToken, profile, cb) => {
try {
const oldUser = await User.findOne({ email: profile.emails[0].value });
if (oldUser) {
return cb(null, oldUser);
}
} catch (err) {
console.log(err);
}
try {
const newUser = await new User({
provider: 'google',
googleId: profile.id,
username: profile.name.givenName,
email: profile.emails[0].value,
emailVerified: profile.emails[0].verified,
name: `${profile.name.givenName} ${profile.name.familyName}`,
avatar: profile.photos[0].value
}).save();
cb(null, newUser);
} catch (err) {
console.log(err);
}
}
);
passport.use(googleLogin);

Some files were not shown because too many files have changed in this diff Show More