diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/README.md b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/README.md new file mode 100644 index 000000000..fe100effa --- /dev/null +++ b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/README.md @@ -0,0 +1,37 @@ +## open ai chat backend configuration and information + +## Module description + +Enter a module description here. + +## Features + +- [ ] This module includes migrations. +- [ ] This module includes environment variables. +- [ ] This module requires manual configurations. +- [ ] This module can be configured with module options. + +## Environment variables + +```properties +ENV_VAR="value" +``` + +## 3rd party setup + +Create account... + +Include screenshots if possible here. + +## Dependencies + +Link to the READMEs of the packages that you used in this module. + +Dependencies used: +- package-name and link to the package + +## API details + +| Api Name | Param | Description | +| ------------------------------ |:------------:|:---------------------------------------------------------------| +| `/modules/module-name/endpoint/` | object `{something: 'string'}` | Description here.| diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/__init__.py b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/admin.py b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/admin.py new file mode 100644 index 000000000..e310b667d --- /dev/null +++ b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/admin.py @@ -0,0 +1,87 @@ +from django.contrib import admin +from .models import Conversation, Message, OpenAIConfig, UserConfig + + +class MessageInline(admin.TabularInline): + model = Message + extra = 0 + readonly_fields = ('conversation', 'role', 'content', 'response','created_at', 'updated_at') + fieldsets = ( + (None, { + 'fields': ('conversation', 'role', 'content', 'response') + }), + ('Dates', { + 'fields': ('created_at', 'updated_at'), + }), + ) + +class ConversationAdmin(admin.ModelAdmin): + list_display = ('session', 'user', 'summary', 'created_at', 'updated_at') + search_fields = ('session', 'user', 'summary', 'created_at', 'updated_at') + readonly_fields = ('created_at', 'updated_at') + ordering = ('-created_at',) + list_filter = ('session', 'user', 'summary', 'created_at', 'updated_at') + fieldsets = ( + (None, { + 'fields': ('session', 'user', 'summary') + }), + ('Dates', { + 'fields': ('created_at', 'updated_at'), + }), + ) + inlines = [ + MessageInline, + ] +admin.site.register(Conversation, ConversationAdmin) + +class MessageAdmin(admin.ModelAdmin): + list_display = ('conversation', 'role', 'content', 'response', 'created_at', 'updated_at') + search_fields = ('conversation', 'role', 'content', 'response', 'created_at', 'updated_at') + readonly_fields = ('created_at', 'updated_at') + ordering = ('-created_at',) + list_filter = ('conversation', 'role', 'content', 'response', 'created_at', 'updated_at') + fieldsets = ( + (None, { + 'fields': ('conversation', 'role', 'content', 'response') + }), + ('Dates', { + 'fields': ('created_at', 'updated_at'), + }), + ) +admin.site.register(Message, MessageAdmin) + + +class OpenAIConfigAdmin(admin.ModelAdmin): + list_display = ('model', 'max_tokens', 'use_context', 'context_length', 'only_questions_in_context', 'summarize_context', 'initial_context', 'is_active', 'global_config', 'created_at', 'updated_at') + search_fields = ('model', 'max_tokens', 'use_context', 'context_length', 'only_questions_in_context', 'summarize_context', 'initial_context', 'is_active', 'global_config', 'created_at', 'updated_at') + readonly_fields = ('created_at', 'updated_at') + ordering = ('-created_at',) + list_filter = ('model', 'max_tokens', 'use_context', 'context_length', 'only_questions_in_context', 'summarize_context', 'initial_context', 'is_active', 'global_config', 'created_at', 'updated_at') + fieldsets = ( + (None, { + 'fields': ('model', 'max_tokens', 'use_context', 'context_length', 'only_questions_in_context', 'summarize_context', 'initial_context', 'is_active', 'global_config') + }), + ('Dates', { + 'fields': ('created_at', 'updated_at'), + }), + ) + +admin.site.register(OpenAIConfig, OpenAIConfigAdmin) + + +class UserConfigAdmin(admin.ModelAdmin): + list_display = ('user', 'config', 'created_at', 'updated_at') + search_fields = ('user', 'config', 'created_at', 'updated_at') + readonly_fields = ('created_at', 'updated_at') + ordering = ('-created_at',) + list_filter = ('user', 'config', 'created_at', 'updated_at') + fieldsets = ( + (None, { + 'fields': ('user', 'config') + }), + ('Dates', { + 'fields': ('created_at', 'updated_at'), + }), + ) + +admin.site.register(UserConfig, UserConfigAdmin) \ No newline at end of file diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/apps.py b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/apps.py new file mode 100644 index 000000000..e69de29bb diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/migrations/0001_initial.py b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/migrations/0001_initial.py new file mode 100644 index 000000000..98af7c0be --- /dev/null +++ b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/migrations/0001_initial.py @@ -0,0 +1,41 @@ +# Generated by Django 3.2.23 on 2023-12-12 22:43 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Conversation', + fields=[ + ('session', models.CharField(blank=True, max_length=255, null=True)), + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('summary', models.TextField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Message', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('role', models.CharField(blank=True, max_length=255, null=True)), + ('content', models.TextField()), + ('response', models.TextField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('conversation', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='open_ai_chat.conversation')), + ], + ), + ] diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/migrations/0002_openaiconfig_userconfig.py b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/migrations/0002_openaiconfig_userconfig.py new file mode 100644 index 000000000..d2d11d06a --- /dev/null +++ b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/migrations/0002_openaiconfig_userconfig.py @@ -0,0 +1,48 @@ +# Generated by Django 3.2.23 on 2023-12-23 04:32 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('open_ai_chat', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='OpenAIConfig', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('model', models.CharField(blank=True, choices=[('gpt-3.5-turbo', 'gpt-3.5-turbo'), ('gpt-3.5-turbo-1106', 'gpt-3.5-turbo-1106'), ('gpt-3.5-turbo-16k', 'gpt-3.5-turbo-16k'), ('gpt-4-0314', 'gpt-4-0314')], max_length=255, null=True)), + ('max_tokens', models.IntegerField(blank=True, default=1000, null=True)), + ('use_context', models.BooleanField(default=True, help_text='Use context from previous messages')), + ('context_length', models.IntegerField(blank=True, default=3, help_text='Number of previous messages to use as context', null=True)), + ('only_questions_in_context', models.BooleanField(default=False, help_text='Only use questions in context')), + ('summarize_context', models.BooleanField(default=True, help_text='use summary as context')), + ('initial_context', models.TextField(blank=True, help_text='Initial context to use e.g. You are a crowdbotics assistant.', null=True)), + ('is_active', models.BooleanField(default=True, help_text='Is this config active')), + ('global_config', models.BooleanField(default=False, help_text='Is this config global')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'OpenAI Config', + 'verbose_name_plural': 'OpenAI Configs', + 'unique_together': {('is_active', 'global_config')}, + }, + ), + migrations.CreateModel( + name='UserConfig', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('config', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='open_ai_chat.openaiconfig')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/migrations/__init__.py b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/models.py b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/models.py new file mode 100644 index 000000000..99b7e9910 --- /dev/null +++ b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/models.py @@ -0,0 +1,62 @@ +import uuid +from django.db import models +from django.conf import settings + +class Conversation(models.Model): + session = models.CharField(max_length=255, null=True, blank=True) + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True) + uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + summary = models.TextField(null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self) -> str: + return f"{self.session}" + + +class Message(models.Model): + conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE, null=True, blank=True) + role = models.CharField(max_length=255, null=True, blank=True) + content = models.TextField() + response = models.TextField(null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self) -> str: + return f"{self.id} - {self.content}" + + +class OpenAIConfig(models.Model): + + MODELS = [ + ("gpt-3.5-turbo", "gpt-3.5-turbo"), + ("gpt-3.5-turbo-1106", "gpt-3.5-turbo-1106"), + ("gpt-3.5-turbo-16k", "gpt-3.5-turbo-16k"), + ("gpt-4-0314", "gpt-4-0314") + ] + + model = models.CharField(max_length=255, null=True, blank=True, choices=MODELS) + max_tokens = models.IntegerField(null=True, blank=True, default=1000) + use_context = models.BooleanField(default=True, help_text="Use context from previous messages") + context_length = models.IntegerField(null=True, blank=True, default=3, help_text="Number of previous messages to use as context") + only_questions_in_context = models.BooleanField(default=False, help_text="Only use questions in context") + summarize_context = models.BooleanField(default=True, help_text="use summary as context") + initial_context = models.TextField(null=True, blank=True, help_text="Initial context to use e.g. You are a crowdbotics assistant.") + is_active = models.BooleanField(default=True, help_text="Is this config active") + global_config = models.BooleanField(default=False, help_text="Is this config global") + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self) -> str: + return f"{self.id} - {self.model}" + + class Meta: + verbose_name = "OpenAI Config" + verbose_name_plural = "OpenAI Configs" + + +class UserConfig(models.Model): + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True) + config = models.ForeignKey(OpenAIConfig, on_delete=models.CASCADE, null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) \ No newline at end of file diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/serializers.py b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/serializers.py new file mode 100644 index 000000000..2a9e6bdd5 --- /dev/null +++ b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/serializers.py @@ -0,0 +1,3 @@ +from rest_framework import serializers + +# your serializer here diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/services/OpenAI.py b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/services/OpenAI.py new file mode 100644 index 000000000..fa5d2a348 --- /dev/null +++ b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/services/OpenAI.py @@ -0,0 +1,30 @@ +import os +from openai import OpenAI as core_openai + +class OpenAI: + client = None + model = None + max_tokens = 1000 + def __init__(self, api_key=None, model='gpt-3.5-turbo'): + self.api_key = api_key or os.environ.get("OPENAI_API_KEY", "") + self.client = core_openai( + api_key=self.api_key, + ) + self.model = model + + # todo: calculate number of tokens based on question length + context length + model + # todo: add context to the question + + def ask(self, question, context=[]): + messages = context + messages.append({"role": "user", "content": question}) + messages.append({ + "role": "system", + "content": "follow the rules: rule 1. short 2-3 liner chat type answers. rule 2. you must iclude a summary at the end or every message, summary should include context from provided system message, current question and answer to be used as context for next message.", + }) + response = self.client.chat.completions.create( + messages=messages, + model=self.model, + max_tokens=self.max_tokens, + ) + return response.choices[0].message.content \ No newline at end of file diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/services/__init__.py b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/services/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/urls.py b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/urls.py new file mode 100644 index 000000000..228067f16 --- /dev/null +++ b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/urls.py @@ -0,0 +1,9 @@ +from django.urls import path, include +from rest_framework import routers +from .viewsets import ConversationViewSet + +router = routers.DefaultRouter() +router.register(r'chat', ConversationViewSet, basename='openai-chat') +urlpatterns = [ + path('', include(router.urls)), +] \ No newline at end of file diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/viewsets.py b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/viewsets.py new file mode 100644 index 000000000..90af33acd --- /dev/null +++ b/modules/open-ai-chat/backend/modules/open_ai_chat/open_ai_chat/viewsets.py @@ -0,0 +1,110 @@ +import os +import json +from rest_framework import viewsets +from rest_framework.views import APIView +from rest_framework import permissions, status +from rest_framework.response import Response + +from .services.OpenAI import OpenAI + +from .models import Conversation, Message, OpenAIConfig + +# Create your views here. +class ConversationViewSet(viewsets.ViewSet): + def list(self, request): + if not request.session.session_key: + request.session.create() + session = request.session.session_key + chat = Conversation.objects.filter(session=session).first() + messages = Message.objects.filter(conversation=chat) + resp = { + 'summary': chat.summary if chat else None, + 'messages': [] + } + """ + [{ + role: 'user', + content: 'That sounds great! How can I get in touch with you?', + created_at: new Date().toISOString(), + },] + """ + + for message in messages: + resp['messages'].append({ + 'role': "user", + 'content': message.content, + 'created_at': message.created_at, + }) + if message.response: + resp['messages'].append({ + 'role': "AI", + 'content': message.response, + 'created_at': message.created_at, + }) + + return Response(resp) + + + def get_context(self, obj): + config = OpenAIConfig.objects.filter(is_active=True, global_config=True).first() + if not config: + return [] + use_context = config.use_context + context_length = config.context_length + only_questions_in_context = config.only_questions_in_context + summarize_context = config.summarize_context + initial_context = config.initial_context + context = [{"role": "system", "content": initial_context},] + if use_context: + messages = Message.objects.filter(conversation=obj).order_by('-created_at')[:context_length] + for message in messages: + if only_questions_in_context and message.content: + context.append({"role": "user", "content": message.content}) + else: + if message.content: + context.append({"role": "user", "content": message.content}) + if message.response: + context.append({"role": "assistant", "content": message.response}) + if summarize_context: + summary = obj.summary + if summary: + context.append({"role": "system", "content": summary}) + return context + + def create(self, request): + if not request.session.session_key: + request.session.create() + session = request.session.session_key + user_id = request.user.id if request.user.is_authenticated else None + q = request.data.get('q') + + conv_obj, created = Conversation.objects.get_or_create(session=session) + if created: + conv_obj.summary = q + if user_id: + conv_obj.user_id = user_id + conv_obj.save() + + context = self.get_context(conv_obj) + + obj = Message.objects.create( + conversation=conv_obj, + role='user', + content=q, + ) + + client = OpenAI() + response = client.ask(q, context) + + # clean response + response = response.split('Summary')[0] + chat_summary = response.split('Summary')[-1].split('summary')[-1] + obj.response = response + conv_obj.summary = chat_summary + obj.save() + conv_obj.save() + + return Response({ + 'q': q, + 'bot': response + }) \ No newline at end of file diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/pyproject.toml b/modules/open-ai-chat/backend/modules/open_ai_chat/pyproject.toml new file mode 100644 index 000000000..7fd26b970 --- /dev/null +++ b/modules/open-ai-chat/backend/modules/open_ai_chat/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/modules/open-ai-chat/backend/modules/open_ai_chat/setup.py b/modules/open-ai-chat/backend/modules/open_ai_chat/setup.py new file mode 100644 index 000000000..ded2ae8c0 --- /dev/null +++ b/modules/open-ai-chat/backend/modules/open_ai_chat/setup.py @@ -0,0 +1,18 @@ +from setuptools import setup +from setuptools.command.build import build + + +# Override build command +class BuildCommand(build): + def initialize_options(self): + build.initialize_options(self) + self.build_base = "/tmp" + + +setup( + name="cb_open_ai_chat", + version="0.1", + packages=["open_ai_chat"], + install_requires=[], + cmdclass={"build": BuildCommand}, +) \ No newline at end of file diff --git a/modules/open-ai-chat/meta.json b/modules/open-ai-chat/meta.json new file mode 100644 index 000000000..be9fd9e49 --- /dev/null +++ b/modules/open-ai-chat/meta.json @@ -0,0 +1,11 @@ +{ + "title": "open ai chat", + "description": "This is a simple chatbot like ChatGPT that uses the OpenAI API to generate responses.", + "root": "/", + "schema": { + "buttonText": { + "type": "string", + "minLength": 1 + } + } +} diff --git a/modules/open-ai-chat/modules/open-ai-chat/README.md b/modules/open-ai-chat/modules/open-ai-chat/README.md new file mode 100644 index 000000000..4214e9601 --- /dev/null +++ b/modules/open-ai-chat/modules/open-ai-chat/README.md @@ -0,0 +1,64 @@ +# open ai chat React native specs + +## Module description + +Enter a module description here. + +Include module's main features here and describe them. + +Include preview screenshots or videos here. + +## Features + +- [ ] This module includes environment variables. +- [ ] This module requires manual configurations. +- [ ] This module can be configured with module options. +- [ ] This module requires manual Android setup. +- [ ] This module requires manual iOS setup. + +## 3rd party setup + +Create account... + +Include screenshots if possible here. + +## Dependencies + +Link to the READMEs of the packages that you used in this module. + +Dependencies used: +- package-name and link to the package + +## Module Options + +### Global Configs + +Update the ``options/options.js`` file with your app's backend url. For example, if your app is called `my-app` and has a url of `https://my-app.botics.co`, your options.js file should look like this: +```js +export const globalOptions = { + ... + url: "https://my-app.botics.co", + ... +} +``` + +### Local Configs + +Update the value of the option in `modules/open-ai-chat/options.js`: +```js +const text = "I understand ..."; +``` + +## Manual Setup + +### Android setup + +Update your `android/app/build.gradle` file: +``` +``` +Update your `android/app/src/main/AndroidManifest.xml` file: + + +### iOS setup + +Update your `ios/open-ai-chat/Info.plist` file: diff --git a/modules/open-ai-chat/modules/open-ai-chat/index.js b/modules/open-ai-chat/modules/open-ai-chat/index.js new file mode 100644 index 000000000..2d3e4f1cc --- /dev/null +++ b/modules/open-ai-chat/modules/open-ai-chat/index.js @@ -0,0 +1,175 @@ +import React, { useRef, useEffect, useState } from "react"; +import { TextInput, Button, List, Avatar, Card } from "react-native-paper"; +import { View, ScrollView, StyleSheet, Dimensions } from "react-native"; +import { useDispatch } from "react-redux"; +import { getMessages, sendMessage } from "../../store/open-api/open-api.slice"; +const isMobileScreen = Dimensions.get("window").width <= 600; // Adjust the threshold as needed + +const FeatureName = () => { + const [messages, setMessages] = useState([ + + ]); + + const dispatch = useDispatch(); + useEffect(() => { + dispatch(getMessages()).then((res) => { + console.log(res); + setMessages(res.payload?.messages); + }); + }, []); + + const [newMessage, setNewMessage] = useState(""); + const scrollViewRef = useRef(null); + useEffect(() => { + // Auto-scroll to the bottom when messages change + console.log("scrolling to end"); + scrollViewRef.current.scrollToEnd({ animated: true }); + }, []); + const handleSend = () => { + if (newMessage.trim() !== "") { + const userMessage = { + q: newMessage + }; + + const message = { + role: "user", + content: newMessage, + created_at: new Date().toISOString() + }; + + setMessages([...messages, message]); // Reverse the order + setNewMessage(""); + + dispatch(sendMessage(userMessage)).then((res) => { + const aiResponse = { + role: "AI", + content: res.payload?.bot, + created_at: new Date().toISOString() + }; + message.response = res.payload?.response; + setMessages([...messages, message, aiResponse]); + }); + } + }; + useEffect(() => { + // Auto-scroll to the bottom when messages change + console.log("scrollViewRef", scrollViewRef); + console.log("scrollViewRef.current", scrollViewRef.current); + scrollViewRef.current.scrollToEnd({ animated: true }); + }, [messages]); + return ( + + {/* Chat messages */} + { + console.log("scrolling to endccc"); + }} + showsHorizontalScrollIndicator={false} + showsVerticalScrollIndicator={false} + > + + Chat + {messages.map((message, index) => ( + + ( + + )} + style={styles.messageItem} + /> + + ))} + + + + {/* Input card for typing and sending messages */} + + + + setNewMessage(text)} + style={{ flex: 1 }} + onSubmitEditing={handleSend} + /> + + + + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + backgroundColor: "#444", + height: "100vh" + }, + messagesContainer: { + flex: 1, + paddingHorizontal: 16, + paddingBottom: 60, // Adjust as needed + width: isMobileScreen ? "95%" : "80%", + alignSelf: "center" + }, + messagesContentContainer: { + flexGrow: 1, + // justifyContent: 'flex-start', + justifyContent: "flex-end" + }, + messageItem: { + marginBottom: 16 + }, + avatar: { + backgroundColor: "blue" // Customize the avatar color + }, + messageTitle: { + fontWeight: "bold", + marginRight: 8 + }, + inputContainer: { + position: "sticky", + bottom: 0, + padding: 16, + width: "70%", + alignSelf: "center" + }, + card: { + padding: 0, + borderRadius: 16 + }, + buttonContainer: { + flexDirection: "row", + justifyContent: "flex-end", + marginLeft: 16 + }, + sendButton: { + width: 50, + height: 50, + borderRadius: 25, + justifyContent: "center", + alignItems: "center" + } +}); + +export default { + title: "FeatureName", + navigator: FeatureName +}; diff --git a/modules/open-ai-chat/modules/open-ai-chat/options.js b/modules/open-ai-chat/modules/open-ai-chat/options.js new file mode 100644 index 000000000..19c067cac --- /dev/null +++ b/modules/open-ai-chat/modules/open-ai-chat/options.js @@ -0,0 +1,16 @@ +import { StyleSheet } from "react-native"; + +const styles = StyleSheet.create({ + heading: { + height: 60, + backgroundColor: "#333333", + padding: 20, + alignItems: "flex-end", + flexDirection: "row", + justifyContent: "center" + } +}); + +export default { + styles: styles +}; diff --git a/modules/open-ai-chat/modules/open-ai-chat/package.json b/modules/open-ai-chat/modules/open-ai-chat/package.json new file mode 100644 index 000000000..f0d3ce770 --- /dev/null +++ b/modules/open-ai-chat/modules/open-ai-chat/package.json @@ -0,0 +1,15 @@ +{ + "name": "@modules/open-ai-chat", + "version": "1.0.1", + "description": "open ai chat", + "private": true, + "main": "index.js", + "author": "Crowdbotics", + "license": "ISC", + "dependencies": { + + }, + "x-dependencies": { + + } +} \ No newline at end of file diff --git a/modules/open-ai-chat/preview.png b/modules/open-ai-chat/preview.png new file mode 100644 index 000000000..3405198e9 Binary files /dev/null and b/modules/open-ai-chat/preview.png differ diff --git a/modules/open-ai-chat/store/open-api/api.js b/modules/open-ai-chat/store/open-api/api.js new file mode 100644 index 000000000..82426545d --- /dev/null +++ b/modules/open-ai-chat/store/open-api/api.js @@ -0,0 +1,37 @@ +import { getGlobalOptions } from "@options"; +import axios from "axios"; + +const global = getGlobalOptions(); +const BASE_URL = global.url; // change your BASE_URL in `options/options.js` to edit this value + +export const sendMessage = async payload => { +// const token = await AsyncStorage.getItem("accessToken"); + const config = { + method: "post", + maxBodyLength: Infinity, + url: `${BASE_URL}/modules/open-ai-chat/chat/`, + headers: { + // Authorization: `Token ${token}` + }, + data: payload + }; + return axios.request(config); +}; + +export const getMessages = async () => { +// const token = await AsyncStorage.getItem("accessToken"); + const config = { + method: "get", + maxBodyLength: Infinity, + url: `${BASE_URL}/modules/open-ai-chat/chat/`, + headers: { + // Authorization: `Token ${token}` + } + }; + return axios.request(config); +}; + +export const api = { + getMessages, + sendMessage +}; diff --git a/modules/open-ai-chat/store/open-api/open-api.slice.js b/modules/open-ai-chat/store/open-api/open-api.slice.js new file mode 100644 index 000000000..c3bcf8d1b --- /dev/null +++ b/modules/open-ai-chat/store/open-api/open-api.slice.js @@ -0,0 +1,96 @@ +import { Alert } from "react-native"; +import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; +import { api } from "./api"; + +export const getMessages = createAsyncThunk("home/getMessages", async () => { + try { + const response = await api.getMessages(); + return response.data; + } catch (error) { + throw new Error(); + } +}); + +export const sendMessage = createAsyncThunk( + "home/sendMessage", + async payload => { + try { + const response = await api.sendMessage(payload); + console.log("response, Success", response); + Alert.alert("Success", response?.data?.message); + return response.data; + } catch (error) { + if (error.message === "Network Error") { + Alert.alert("Failed", "Please check your internet connection"); + } + throw new Error(); + } + } +); + +const initialState = { + getMessages: { + entities: { + data: [] + }, + api: { + loading: "idle", + error: null + } + }, + sendMessage: { + entities: [], + api: { + loading: "idle", + error: null + } + } +}; +const openAPIChatSlice = createSlice({ + name: "openAPIchat", + initialState: initialState, + reducers: {}, + extraReducers: { + [getMessages.pending]: state => { + if (state.getMessages.api.loading === "idle") { + state.getMessages.api.loading = "pending"; + state.getMessages.api.error = null; + } + }, + [getMessages.fulfilled]: (state, action) => { + if (state.getMessages.api.loading === "pending") { + state.getMessages.entities.data = action.payload; + state.getMessages.api.loading = "idle"; + } + }, + [getMessages.rejected]: (state, action) => { + if (state.getMessages.api.loading === "pending") { + state.getMessages.api.error = action.error; + state.getMessages.api.loading = "idle"; + } + }, + + [sendMessage.pending]: state => { + if (state.sendMessage.api.loading === "idle") { + state.sendMessage.api.loading = "pending"; + state.sendMessage.api.error = null; + } + }, + [sendMessage.fulfilled]: (state, action) => { + if (state.sendMessage.api.loading === "pending") { + state.sendMessage.entities = action.payload.value; + state.sendMessage.api.loading = "idle"; + } + }, + [sendMessage.rejected]: (state, action) => { + if (state.sendMessage.api.loading === "pending") { + state.sendMessage.api.error = action.error; + state.sendMessage.api.loading = "idle"; + } + } + } +}); + +export default { + slice: openAPIChatSlice +};