deepakpant commited on
Commit
a532053
·
0 Parent(s):

Initial Project structure

Browse files
.devcontainer/devcontainer.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Expressly AI Agent",
3
+ // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
4
+ "image": "python:3.12-slim",
5
+ "features": {
6
+ "ghcr.io/devcontainers/features/python:1": {
7
+ "version": "3.12"
8
+ },
9
+ "ghcr.io/devcontainers/features/git:1": {},
10
+ "ghcr.io/devcontainers/features/docker-in-docker:2.12.0": {
11
+ "version": "latest",
12
+ "moby": true
13
+ }
14
+ },
15
+ // Use 'postCreateCommand' to run commands after the container is created.
16
+ "postCreateCommand": "./.devcontainer/postCreateCommand.sh",
17
+ "forwardPorts": [
18
+ 7860,
19
+ 80
20
+ ],
21
+ // Configure tool-specific properties.
22
+ "customizations": {
23
+ "vscode": {
24
+ "extensions": [
25
+ "ms-python.python",
26
+ "editorconfig.editorconfig",
27
+ "ms-azuretools.vscode-docker", // Docker
28
+ "ms-python.isort", // isort
29
+ "visualstudioexptteam.vscodeintellicode", // IntelliCode
30
+ "codeium.codeium", // Codeium AI
31
+ "ms-vscode.makefile-tools", // Makefile tool
32
+ "ms-python.python", // Python
33
+ "ms-python.black-formatter", // Black
34
+ "ms-python.debugpy", // Debugger for Python
35
+ "redhat.vscode-yaml", // YAML
36
+ "tamasfe.even-better-toml" // TOML
37
+ ]
38
+ }
39
+ }
40
+ }
.devcontainer/postCreateCommand.sh ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #! /usr/bin/env bash
2
+
3
+ # Install fish terminal
4
+ sudo apt update -y
5
+ sudo apt-get install fish -y
6
+
7
+ # Repo Initialization
8
+ git config --global --add safe.directory /workspaces/expressly
9
+
10
+ # Install Dependencies
11
+ pip install crewai
.dockerignore ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled Python files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+ .pytest_cache/
7
+ .mypy_cache/
8
+
9
+ # Environment variables and config files
10
+ .env
11
+ .env.*
12
+
13
+ # Virtual environment directories
14
+ venv/
15
+ env/
16
+ .virtualenv/
17
+ .venv/
18
+
19
+ # System files
20
+ .DS_Store
21
+ Thumbs.db
22
+
23
+ # Logs and debug files
24
+ *.log
25
+ *.out
26
+ *.err
27
+
28
+ # Cache and coverage files
29
+ *.egg-info/
30
+ .eggs/
31
+ *.cache
32
+ *.coverage
33
+ .coverage.*
34
+ .cache/
35
+ .pytest_cache/
36
+ nosetests.xml
37
+ coverage.xml
38
+ .ruff_cache/
39
+
40
+ # Build files and directories
41
+ build/
42
+ dist/
43
+ *.egg
44
+ *.whl
45
+
46
+ # Temporary files
47
+ *.swp
48
+ *.swo
49
+ *.bak
50
+ *.tmp
51
+
52
+ # Docker-specific files
53
+ docker-compose.override.yml
54
+
55
+ # Other files to exclude
56
+ .idea/
57
+ .vscode/
58
+ *.iml
59
+ .DS_Store
60
+
61
+ # Git files
62
+ .git
63
+ .github
64
+ .gitignore
65
+ .gitattributes
66
+
67
+ # Other files
68
+ uv.lock
.env.example ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ MODEL=gemini/gemini-1.5-flash
2
+ GEMINI_API_KEY=<gemini_api_key> # Your API key here
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.github/workflows/build-container.yml ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Build and Deploy Docker Image
2
+
3
+ on:
4
+ workflow_dispatch: {}
5
+ push:
6
+ branches:
7
+ - main # or master, depending on your default branch
8
+
9
+ env:
10
+ DOCKER_IMAGE: deepak93p/expressly-app # Replace with your DockerHub image
11
+
12
+
13
+ jobs:
14
+ build-push-docker-image:
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - name: Checkout repository
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Login to Docker Hub
22
+ env:
23
+ DOCKER_USER: deepak93p
24
+ DOCKER_PWD: ${{ secrets.DOCKERHUB_PUSH_TOKEN }}
25
+ run: echo $DOCKER_PWD | docker login -u $DOCKER_USER --password-stdin
26
+
27
+ - name: Build and Push Docker Image
28
+ run: |
29
+ docker build -t ${{ env.DOCKER_IMAGE }}:latest -f Dockerfile .
30
+ docker push ${{ env.DOCKER_IMAGE }}:latest
31
+
32
+ - name: Clean up Docker system
33
+ run: docker system prune -f
.github/workflows/hugging_face-deploy.yml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Deploy to Hugging Face Spaces
2
+
3
+ on:
4
+ workflow_dispatch: {}
5
+ push:
6
+ branches:
7
+ - main # or master, depending on your default branch
8
+
9
+ jobs:
10
+ deploy:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout repository
14
+ uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0
17
+
18
+ - name: Configure Git
19
+ run: |
20
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
21
+ git config --global user.name "github-actions[bot]"
22
+
23
+ - name: Push to Hugging Face Space
24
+ env:
25
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
26
+ run: |
27
+ git remote add space https://USER:[email protected]/spaces/deepakpant/expressly-app
28
+ git push --force space main
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ .env
2
+ __pycache__/
.vscode/launch.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "Python Debugger: expressly",
6
+ "type": "debugpy",
7
+ "request": "launch",
8
+ "program": "${workspaceFolder}/src/expressly_server/web_app.py",
9
+ "args": ["run"],
10
+ "console": "integratedTerminal",
11
+ "justMyCode": true,
12
+ "jinja": true
13
+ }
14
+ ]
15
+ }
.vscode/settings.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "files.exclude": {
3
+ "**/.mypy_cache": true,
4
+ "**/__pycache__": true,
5
+ "**/.pytest_cache": true,
6
+ "**/.ruff_cache": true,
7
+ "**/.venv": true,
8
+ "**/venv": true,
9
+ "**/node_modules": true,
10
+ "**/site": true,
11
+ "**/.coverage": true,
12
+ "**/coverage.xml": true,
13
+ "**/build": true,
14
+ "**/dist": true
15
+ }
16
+ }
Dockerfile ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Python image as a base
2
+ FROM python:3.12-slim
3
+
4
+ RUN useradd -m -u 1000 user
5
+
6
+ # Install system dependencies
7
+ RUN apt-get update && apt-get install -y \
8
+ build-essential \
9
+ curl \
10
+ gcc \
11
+ libssl-dev \
12
+ pkg-config \
13
+ && rm -rf /var/lib/apt/lists/*
14
+
15
+ # Install Rust (required for orjson and other Rust-based Python packages)
16
+ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
17
+ && export PATH="$HOME/.cargo/bin:$PATH" \
18
+ && rustc --version
19
+
20
+ # Ensure Rust is added to PATH for all subsequent RUN commands
21
+ ENV PATH="/root/.cargo/bin:$PATH"
22
+
23
+ # Set the working directory inside the container
24
+ WORKDIR /app
25
+
26
+ # Copy the pyproject.toml and any other build-related files
27
+ COPY --chown=user pyproject.toml .
28
+
29
+
30
+ # Install dependencies
31
+ RUN pip install --upgrade pip \
32
+ && pip install uv crewai \
33
+ && crewai install
34
+
35
+ # Ensure the /app directory is owned by the non-root user
36
+ RUN chown -R user:user /app
37
+
38
+ # Switch to the non-root user
39
+ USER user
40
+
41
+ # Copy the application code into the container
42
+ COPY --chown=user . .
43
+
44
+ EXPOSE 7860
45
+
46
+ ENV GRADIO_SERVER_NAME="0.0.0.0"
47
+
48
+ # Define the command to run your application
49
+ CMD ["uv", "run", "expressly"]
README.md ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Expressly
3
+ emoji: 🛠️
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: docker
7
+ app_port: 7860
8
+ license: mit
9
+ short_description: Expressly - Text Transformation App
10
+ ---
11
+
12
+ # Expressly - Text Transformation App Backend
13
+
14
+ This document provides an overview of the backend server for the Expressly - Text Transformation App. The app is designed to transform text based on user preferences and descriptions, leveraging a CrewAI-based multi-agent AI application.
15
+
16
+ ## Key Features
17
+
18
+ ### Text Transformation Options
19
+ The backend supports the following transformation capabilities:
20
+ 1. **Formats**
21
+ - For details on supported formats, please refer to the [Expressly Wiki](https://github.com/DeepakPant93/expressly/wiki).
22
+
23
+ 2. **Tones**
24
+ - For details on supported tones, please refer to the [Expressly Wiki](https://github.com/DeepakPant93/expressly/wiki).
25
+
26
+ 3. **Target Audience**
27
+ - For details on supported target audiences, please refer to the [Expressly Wiki](https://github.com/DeepakPant93/expressly/wiki).
28
+
29
+ ### Output
30
+ The backend processes user inputs and generates formatted text tailored to the specified preferences. Users can easily copy and utilize the output.
31
+
32
+ ## Backend Configuration
33
+
34
+ ### Prerequisites
35
+ 1. Install `uv` if not already installed:
36
+ ```bash
37
+ pip install uv
38
+ ```
39
+ 2. Navigate to your project directory and install dependencies:
40
+ ```bash
41
+ crewai install
42
+ ```
43
+ (Optional: Lock dependencies using the CLI command.)
44
+
45
+ ### Customization
46
+ 1. Add environment variables to the `.env` file:
47
+ ```plaintext
48
+ MODEL=gemini/gemini-1.5-flash
49
+ GEMINI_API_KEY=<gemini_api_key> # Your API key here
50
+ ```
51
+ 2. Modify configuration files as needed:
52
+ - `src/expressly_server/config/agents.yaml`: Define your agents.
53
+ - `src/expressly_server/config/tasks.yaml`: Define your tasks.
54
+ - `src/expressly_server/crew.py`: Add custom logic, tools, and arguments.
55
+ - `src/expressly_server/main.py`: Customize inputs for agents and tasks.
56
+
57
+ ### Running the Backend
58
+ To start the backend server and execute tasks:
59
+ ```bash
60
+ crewai run
61
+ ```
62
+
63
+ ## Additional Notes
64
+ - Ensure all environment variables are correctly set in the `.env` file.
65
+ - Regularly update your agents and tasks configuration to enhance functionality.
66
+ - Refer to the CrewAI documentation for advanced customizations.
67
+
68
+ This backend server powers the text transformation capabilities of Expressly, making it adaptable to a wide range of use cases.
knowledge/content_style_mapping.json ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "target_audience": {
3
+ "linkedin_post": {
4
+ "format": "post",
5
+ "tone": "professional"
6
+ },
7
+ "whatsapp_message": {
8
+ "format": "chat",
9
+ "tone": "casual"
10
+ },
11
+ "tweet": {
12
+ "format": "tweet",
13
+ "tone": "straightforward"
14
+ },
15
+ "news_article": {
16
+ "format": "article",
17
+ "tone": "neutral"
18
+ },
19
+ "technical_blog": {
20
+ "format": "blog",
21
+ "tone": "professional"
22
+ },
23
+ "formal_email": {
24
+ "format": "email",
25
+ "tone": "professional"
26
+ },
27
+ "instagram_post": {
28
+ "format": "post",
29
+ "tone": "friendly"
30
+ },
31
+ "website_content": {
32
+ "format": "report",
33
+ "tone": "neutral"
34
+ },
35
+ "marketing_email": {
36
+ "format": "email",
37
+ "tone": "confident"
38
+ },
39
+ "job_application": {
40
+ "format": "email",
41
+ "tone": "professional"
42
+ },
43
+ "customer_support_response": {
44
+ "format": "chat",
45
+ "tone": "friendly"
46
+ }
47
+ }
48
+ }
knowledge/format.json ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "format": {
3
+ "post": {
4
+ "name": "Post",
5
+ "description": "A short and engaging content piece, often used for platforms like LinkedIn or Facebook. It highlights key points and encourages interaction, such as likes, shares, or comments.",
6
+ "max_length": 300,
7
+ "negative": "Avoid long paragraphs, excessive technical jargon, or overly formal language. Do not make the content too detailed or complex."
8
+ },
9
+ "chat": {
10
+ "name": "Chat",
11
+ "description": "A conversational and interactive response that mimics real-time messaging. It focuses on direct, clear, and natural communication with a friendly tone.",
12
+ "max_length": 200,
13
+ "negative": "Do not use overly formal language, complex sentence structures, or impersonal tones. Avoid too much technical information and hashtags."
14
+ },
15
+ "tweet": {
16
+ "name": "Tweet",
17
+ "description": "A concise and impactful message designed for Twitter or similar platforms. It often uses hashtags, mentions, or trending topics to increase visibility.",
18
+ "max_length": 280,
19
+ "negative": "Avoid lengthy explanations, irrelevant details, or complex sentences. Do not exceed the character limit or use a formal tone."
20
+ },
21
+ "email": {
22
+ "name": "Email",
23
+ "description": "A formal or semi-formal written message for direct communication. It includes a clear subject line, opening, body, and closing, often addressing specific recipients.",
24
+ "max_length": 1500,
25
+ "negative": "Avoid casual or overly brief content. Do not make the email too long or include excessive details that deviate from the purpose."
26
+ },
27
+ "blog": {
28
+ "name": "Blog",
29
+ "description": "A detailed and informative piece written for online readers. It includes a compelling introduction, multiple sections with headings, and a conclusion to engage and educate the audience.",
30
+ "max_length": 2000,
31
+ "negative": "Avoid overly technical language, jargon, or too formal language. Do not make the blog too short or lacking in detail."
32
+ },
33
+ "article": {
34
+ "name": "Article",
35
+ "description": "A comprehensive, well-researched write-up, often featuring in-depth analysis or expert perspectives. Suitable for magazines, websites, or journals to inform or persuade a broad audience.",
36
+ "max_length": 3000,
37
+ "negative": "Do not oversimplify complex topics, avoid using unverified sources or vague statements. Avoid making the article too conversational or informal."
38
+ },
39
+ "report": {
40
+ "name": "Report",
41
+ "description": "A structured document that presents facts, data, and analysis for a specific audience or purpose. It often includes charts, tables, and a clear summary of findings.",
42
+ "max_length": 5000,
43
+ "negative": "Avoid informal language, speculative content, or a lack of data. Do not make the report subjective or unorganized."
44
+ }
45
+ }
46
+ }
knowledge/target_audience.json ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "target_audience": {
3
+ "linkedin_post": {
4
+ "name": "LinkedIn Post",
5
+ "description": "Aimed at professionals, thought leaders, and businesses. Content is typically career-focused or industry-related, designed to spark engagement among people seeking professional growth, networking, or knowledge.",
6
+ "ideal_audience": "Business professionals, marketers, entrepreneurs, recruiters, job seekers, industry experts"
7
+ },
8
+ "whatsapp_message": {
9
+ "name": "WhatsApp Message",
10
+ "description": "Casual or semi-formal communication meant for direct, personal messaging. Used for sending quick updates, reminders, or messages to small groups, often informal in tone.",
11
+ "ideal_audience": "Friends, family, small teams, personal contacts, colleagues in a more relaxed setting"
12
+ },
13
+ "tweet": {
14
+ "name": "Tweet",
15
+ "description": "Aimed at a broad, diverse audience on Twitter, from the general public to niche communities. Tweets are brief and designed to provoke quick engagement, whether through likes, retweets, or comments.",
16
+ "ideal_audience": "General public, influencers, content creators, tech enthusiasts, trend followers, entertainment fans"
17
+ },
18
+ "news_article": {
19
+ "name": "News Article",
20
+ "description": "Targeted at a wide audience seeking timely, relevant information. News articles are often written for readers who are looking for the latest updates, in-depth analysis, or expert opinions on current events or trending topics.",
21
+ "ideal_audience": "General public, journalists, news enthusiasts, academics, policy makers, professionals interested in current affairs"
22
+ },
23
+ "technical_blog": {
24
+ "name": "Technical Blog",
25
+ "description": "Aimed at professionals, experts, or hobbyists in specialized fields like technology, software development, engineering, or data science. The content is typically in-depth, offering insights, tutorials, and technical knowledge.",
26
+ "ideal_audience": "Software developers, engineers, data scientists, tech entrepreneurs, IT professionals, learners seeking advanced technical skills"
27
+ },
28
+ "formal_email": {
29
+ "name": "Formal Email",
30
+ "description": "Designed for business or professional communication. The email is meant for communicating with colleagues, clients, business partners, or supervisors in a polite, respectful, and formal tone.",
31
+ "ideal_audience": "Business professionals, corporate partners, clients, managers, senior leadership, job candidates"
32
+ },
33
+ "instagram_post": {
34
+ "name": "Instagram Post",
35
+ "description": "Aimed at a visually-driven, creative audience. Instagram posts are often used for branding, lifestyle content, promotions, or personal expression, with a focus on imagery and concise captions.",
36
+ "ideal_audience": "Millennials, Gen Z, influencers, lifestyle bloggers, fashion enthusiasts, foodies, beauty influencers, brand followers"
37
+ },
38
+ "website_content": {
39
+ "name": "Website Content",
40
+ "description": "Targeted at a broad range of users, from potential customers to casual visitors. Website content needs to be informative, engaging, and easily navigable, with a focus on conversion and user experience.",
41
+ "ideal_audience": "Consumers, visitors, potential customers, search engine users, anyone looking for information or services"
42
+ },
43
+ "marketing_email": {
44
+ "name": "Marketing Email",
45
+ "description": "Aimed at potential customers or existing leads. Marketing emails are designed to drive action, whether it’s to purchase a product, sign up for a service, or engage with a special offer.",
46
+ "ideal_audience": "Prospective customers, leads, subscribers, clients, consumers interested in sales promotions"
47
+ },
48
+ "job_application": {
49
+ "name": "Job Application",
50
+ "description": "Directed towards hiring managers, recruiters, or HR professionals. The job application content should clearly convey qualifications, skills, and the applicant's interest in a specific role.",
51
+ "ideal_audience": "Recruiters, hiring managers, HR professionals, employers, staffing agencies"
52
+ },
53
+ "customer_support_response": {
54
+ "name": "Customer Support Response",
55
+ "description": "Meant for existing or potential customers seeking assistance with a product or service. The tone should be empathetic, understanding, and solution-oriented, addressing the customer’s issue in a clear, helpful manner.",
56
+ "ideal_audience": "Customers with inquiries, complaints, technical issues, or service requests"
57
+ }
58
+ }
59
+ }
knowledge/tone.json ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "tone": {
3
+ "professional": {
4
+ "name": "Professional",
5
+ "description": "Formal and respectful, suited for workplace communication."
6
+ },
7
+ "casual": {
8
+ "name": "Casual",
9
+ "description": "Relaxed and conversational, suitable for informal interactions."
10
+ },
11
+ "straightforward": {
12
+ "name": "Straightforward",
13
+ "description": "Direct and concise, avoiding unnecessary details."
14
+ },
15
+ "confident": {
16
+ "name": "Confident",
17
+ "description": "Assertive and positive, showing conviction in the message."
18
+ },
19
+ "friendly": {
20
+ "name": "Friendly",
21
+ "description": "Warm and approachable, making the user feel comfortable."
22
+ },
23
+ "neutral": {
24
+ "name": "Neutral",
25
+ "description": "Objective and balanced, without bias or emotion."
26
+ },
27
+ "storytelling": {
28
+ "name": "Storytelling",
29
+ "description": "Narrative style, weaving details into a compelling story."
30
+ },
31
+ "inspirational": {
32
+ "name": "Inspirational",
33
+ "description": "Uplifting and encouraging, motivating the user."
34
+ }
35
+ }
36
+ }
pyproject.toml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "expressly_server"
3
+ version = "0.1.0"
4
+ description = "expressly-server using crewAI"
5
+ authors = [{ name = "Deepak Pant", email = "[email protected]" }]
6
+ requires-python = ">=3.10,<=3.13"
7
+ dependencies = [
8
+ "crewai[tools]>=0.86.0,<1.0.0",
9
+ "gradio>=5.12.0,<5.13.0"
10
+ ]
11
+
12
+ [project.scripts]
13
+ expressly = "expressly_server.web_app:launch"
14
+ run_crew = "expressly_server.main:run"
15
+ train = "expressly_server.main:train"
16
+ replay = "expressly_server.main:replay"
17
+ test = "expressly_server.main:test"
18
+
19
+ [build-system]
20
+ requires = ["hatchling"]
21
+ build-backend = "hatchling.build"
src/expressly_server/__init__.py ADDED
File without changes
src/expressly_server/app.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, responses
2
+ from fastapi.openapi.utils import get_openapi
3
+ from expressly_server.routers.route import router
4
+
5
+ __version__ = "0.0.1"
6
+
7
+ app = FastAPI(
8
+ title="Expressly AI Server",
9
+ description="Expressly is your ultimate tool for transforming text effortlessly.",
10
+ version=__version__,
11
+ docs_url="/docs",
12
+ redoc_url="/redoc",
13
+ )
14
+
15
+
16
+ app = FastAPI()
17
+
18
+
19
+ @app.get("/", include_in_schema=False)
20
+ async def root() -> responses.RedirectResponse:
21
+ """
22
+ Redirects the root URL to the API documentation page.
23
+
24
+ Returns:
25
+ RedirectResponse: A response object that redirects the client to the "/docs" URL.
26
+ """
27
+
28
+ return responses.RedirectResponse("/docs")
29
+
30
+ # Include routers
31
+ app.include_router(router, prefix="/app/v1", tags=["Operations"])
32
+
33
+
34
+ def _custom_openapi() -> dict:
35
+ if app.openapi_schema:
36
+ return app.openapi_schema
37
+ openapi_schema = get_openapi(
38
+ title="Expressly AI Server",
39
+ description="Expressly is your ultimate tool for transforming text effortlessly.",
40
+ version=__version__,
41
+ routes=app.routes,
42
+ )
43
+ app.openapi_schema = openapi_schema
44
+ return app.openapi_schema
45
+
46
+
47
+ app.openapi = _custom_openapi
48
+
49
+
50
+ def main() -> None:
51
+ """
52
+ The main entry point of the application.
53
+
54
+ This function starts the FastAPI server using Uvicorn. It serves the API
55
+ on the specified host and port. The function is intended to be run
56
+ directly when the script is executed.
57
+
58
+ Notes:
59
+ - The 'nosec B104' comment is used to suppress a security warning
60
+ related to binding to all network interfaces.
61
+ """
62
+
63
+ import uvicorn
64
+
65
+ uvicorn.run(app, host="0.0.0.0", port=10000)
66
+
67
+
68
+ if __name__ == "__main__":
69
+ main()
src/expressly_server/config/agents.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ content_creator:
2
+ role: >
3
+ Senior Content Creator
4
+ goal: >
5
+ Created engaging and informative content
6
+ backstory: >
7
+ You're a seasoned content creator with a knack for producing high-quality
8
+ content that captivates and educates your audience. You're known for your
9
+ ability to translate complex concepts into clear and engaging narratives,
10
+ making it easy for others to learn and understand.
src/expressly_server/config/tasks.yaml ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ content_creator_task:
2
+ description: >
3
+ Understand the context carefully
4
+
5
+ ---
6
+ Context:
7
+ ({context})
8
+ ---
9
+
10
+ Describe the context in a way that would follow the tone, format, and guidelines provided.
11
+
12
+ ---
13
+ Tone:(
14
+ [Tone: {tone[name]}] ###
15
+ [Tone Description: {tone[description]}] ###
16
+ )
17
+ ---
18
+
19
+ ---
20
+ Format:(
21
+ [Format Type: {format[name]}] ###
22
+ [Format Description:{format[description]}] ###
23
+ [Max Content Length: {format[max_length]}] ###
24
+ [Negetive: {format[negative]}])
25
+ )
26
+ ---
27
+
28
+ ---
29
+ Target Audience: (
30
+ [Target Audience: {target_audience[name]}] ###
31
+ [Target Audience Description: {target_audience[description]}] ###
32
+ [Ideal Audience Description: {target_audience[ideal_audience]}] ###
33
+ )
34
+ ---
35
+
36
+ Make sure you find the latest information about the topic in the internet if needed and provide a well-researched content.
37
+ expected_output: >
38
+ A fully fledge output with the desised format & tone and the the Guidelines provided in the description.
39
+ Formatted as markdown without '```'
40
+ agent: content_creator
src/expressly_server/crew.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from crewai import Agent, Crew, Process, Task
2
+ from crewai.project import CrewBase, agent, crew, task, before_kickoff
3
+ from crewai.llm import LLM
4
+ from crewai.knowledge.source.json_knowledge_source import JSONKnowledgeSource
5
+ from dotenv import load_dotenv
6
+ import os
7
+ from typing import Dict, Any, Optional
8
+ import json
9
+ from expressly_server.utils.utils import load_json_data, sanitize_input
10
+
11
+ load_dotenv()
12
+
13
+ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
14
+ MODEL = os.getenv("MODEL")
15
+
16
+ FORMAT_JSON_FILE = "format.json"
17
+ TONE_JSON_FILE = "tone.json"
18
+ TARGET_AUDIENCE_JSON_FILE = "target_audience.json"
19
+ CONTENT_STYLE_MAPPING_JSON_FILE = "content_style_mapping.json"
20
+ KNOWLEDGE_SOURCE_PATH = "knowledge"
21
+
22
+
23
+ @CrewBase
24
+ class ExpresslyServer:
25
+ """ExpresslyServer crew"""
26
+
27
+ # Create a knowledge source
28
+ json_knowledge_source = JSONKnowledgeSource(
29
+ file_paths=["format.json", "tone.json", "target_audience.json"],
30
+ )
31
+
32
+ agents_config = "config/agents.yaml"
33
+ tasks_config = "config/tasks.yaml"
34
+
35
+ llm = LLM(model=MODEL, api_key=GEMINI_API_KEY, temperature=0.7)
36
+
37
+ @before_kickoff
38
+ def validate_inputs(
39
+ self, inputs: Optional[Dict[str, Any]]
40
+ ) -> Optional[Dict[str, Any]]:
41
+ """
42
+ Validate and process user inputs based on the active tab selection.
43
+
44
+ This method checks the integrity and presence of required inputs, loads
45
+ necessary JSON data, and validates the active_tab value to ensure the
46
+ appropriate fields are populated. It formats the inputs for further processing.
47
+
48
+ Parameters:
49
+ inputs (Optional[Dict[str, Any]]): The dictionary containing user inputs,
50
+ including 'target_audience', 'format', 'tone', 'active_tab', and 'prompt'.
51
+
52
+ Returns:
53
+ Optional[Dict[str, Any]]: A dictionary formatted with context, format, tone,
54
+ and target_audience details based on the inputs provided.
55
+
56
+ Raises:
57
+ ValueError: If inputs are missing, not a dictionary, or required fields
58
+ ('active_tab', 'prompt', 'format', 'tone', 'target_audience') are not provided
59
+ or invalid.
60
+ """
61
+
62
+ if inputs is None or len(inputs) == 0 or not isinstance(inputs, dict):
63
+ raise ValueError("Inputs is required and must be a dictionary")
64
+
65
+ ## Get the first element from the list of inputs and get the value of target, format, active_tab and prompt
66
+ query = inputs
67
+ target_audience: str = sanitize_input(query.get("target_audience"))
68
+ content_format: str = sanitize_input(query.get("format"))
69
+ content_tone: str = sanitize_input(query.get("tone"))
70
+ prompt: str = query.get("prompt")
71
+
72
+ # Check if prompt are not None
73
+ if prompt is None:
74
+ raise ValueError("Prompt is required")
75
+
76
+ # Load JSON data from content_style_mapping.json
77
+ content_style_mapping_json = load_json_data(
78
+ CONTENT_STYLE_MAPPING_JSON_FILE, KNOWLEDGE_SOURCE_PATH
79
+ )
80
+ tone_json = load_json_data(TONE_JSON_FILE, KNOWLEDGE_SOURCE_PATH)
81
+ format_json = load_json_data(FORMAT_JSON_FILE, KNOWLEDGE_SOURCE_PATH)
82
+ target_audience_json = load_json_data(
83
+ TARGET_AUDIENCE_JSON_FILE, KNOWLEDGE_SOURCE_PATH
84
+ )
85
+
86
+ if target_audience != "":
87
+ ## Resetting the format and tone as per the target audience
88
+ mappings: dict = content_style_mapping_json.get("target_audience").get(
89
+ target_audience
90
+ )
91
+ format_dict = format_json.get("format").get(mappings.get("format"))
92
+ tone_dict = tone_json.get("tone").get(mappings.get("tone"))
93
+ target_audience_dict = target_audience_json.get("target_audience").get(
94
+ target_audience
95
+ )
96
+ elif content_format != "" and content_tone != "":
97
+ ## Constructing a target_audience_dict with empty values and populating the format and tone as per the input
98
+ target_audience_dict = {
99
+ "name": "",
100
+ "description": "",
101
+ "ideal_audience": "",
102
+ }
103
+ format_dict = format_json.get("format").get(content_format)
104
+ tone_dict = tone_json.get("tone").get(content_tone)
105
+ else:
106
+ raise ValueError("Provide either target audience or format and tone")
107
+
108
+ ## Format the inputs
109
+ inputs = {
110
+ "context": prompt,
111
+ "format": format_dict,
112
+ "tone": tone_dict,
113
+ "target_audience": target_audience_dict,
114
+ }
115
+
116
+ return inputs
117
+
118
+ @agent
119
+ def content_creator(self) -> Agent:
120
+ """
121
+ Initializes and returns an Agent for content creation.
122
+
123
+ This agent is configured using predefined settings for the content creator
124
+ and utilizes a language model (LLM) for generating content. The agent
125
+ accesses a JSON knowledge source to enhance its capabilities and operates
126
+ in verbose mode for detailed output logging.
127
+
128
+ Returns:
129
+ Agent: An initialized agent configured for content creation.
130
+ """
131
+
132
+ return Agent(
133
+ config=self.agents_config["content_creator"],
134
+ llm=self.llm,
135
+ knowledge_source=[self.json_knowledge_source],
136
+ verbose=True,
137
+ )
138
+
139
+ @task
140
+ def content_creator_task(self) -> Task:
141
+ """
142
+ Initializes and returns a Task for content creation.
143
+
144
+ This task is configured using predefined settings for content creation
145
+ and is used by the content creator agent to generate content.
146
+
147
+ Returns:
148
+ Task: An initialized task configured for content creation.
149
+ """
150
+ return Task(
151
+ config=self.tasks_config["content_creator_task"],
152
+ )
153
+
154
+ @crew
155
+ def crew(self) -> Crew:
156
+ """Creates the ExpresslyServer crew"""
157
+
158
+ return Crew(
159
+ agents=self.agents,
160
+ tasks=self.tasks,
161
+ process=Process.sequential,
162
+ knowledge_source=[self.json_knowledge_source],
163
+ verbose=True,
164
+ )
src/expressly_server/main.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ import sys
3
+ import warnings
4
+
5
+ from expressly_server.crew import ExpresslyServer
6
+ import dotenv
7
+
8
+ warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd")
9
+
10
+ dotenv.load_dotenv()
11
+
12
+
13
+ def run():
14
+ """
15
+ Run the crew.
16
+ """
17
+
18
+ inputs = {
19
+ "prompt": "I want to thanks DeepLearning and John from the crewAI for this amazing course..",
20
+ "format": "Email",
21
+ "tone": "Friendly",
22
+ "target_audience": "",
23
+ }
24
+
25
+ ExpresslyServer().crew().kickoff(inputs=inputs)
26
+
27
+
28
+ def train():
29
+ """
30
+ Train the crew for a given number of iterations.
31
+ """
32
+ inputs = {"topic": "AI LLMs"}
33
+ try:
34
+ ExpresslyServer().crew().train(
35
+ n_iterations=int(sys.argv[1]), filename=sys.argv[2], inputs=inputs
36
+ )
37
+
38
+ except Exception as e:
39
+ raise Exception(f"An error occurred while training the crew: {e}")
40
+
41
+
42
+ def replay():
43
+ """
44
+ Replay the crew execution from a specific task.
45
+ """
46
+ try:
47
+ ExpresslyServer().crew().replay(task_id=sys.argv[1])
48
+
49
+ except Exception as e:
50
+ raise Exception(f"An error occurred while replaying the crew: {e}")
51
+
52
+
53
+ def test():
54
+ """
55
+ Test the crew execution and returns the results.
56
+ """
57
+ inputs = {"topic": "AI LLMs"}
58
+ try:
59
+ ExpresslyServer().crew().test(
60
+ n_iterations=int(sys.argv[1]), openai_model_name=sys.argv[2], inputs=inputs
61
+ )
62
+
63
+ except Exception as e:
64
+ raise Exception(f"An error occurred while replaying the crew: {e}")
src/expressly_server/routers/route.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter
2
+ from expressly_server.crew import ExpresslyServer
3
+ from expressly_server.schemas.schema import ChatInput, ChatOutput
4
+ from fastapi import HTTPException
5
+
6
+
7
+ router = APIRouter()
8
+
9
+
10
+ @router.post("/chat")
11
+ async def chat(inputs: ChatInput) -> ChatOutput:
12
+
13
+ result = None
14
+
15
+ try:
16
+ outputs = ExpresslyServer().crew().kickoff(inputs=inputs.model_dump())
17
+
18
+ if outputs is None:
19
+ result = "Please check the inputs and try again. If the issue persists, contact support."
20
+ else:
21
+ result = outputs.raw
22
+ except ValueError as ve:
23
+ raise ValueError(f"Value error occurred: {ve}")
24
+ except Exception as e:
25
+ print(f"An Internal server error occurred: {e}")
26
+ raise HTTPException(status_code=500, detail="Internal server error: Please try again later.")
27
+
28
+ return ChatOutput(result=result)
src/expressly_server/schemas/schema.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+ from pydantic.fields import Field
3
+ from typing import Optional
4
+
5
+
6
+ class ChatInput(BaseModel):
7
+ prompt: str = Field(
8
+ ..., min_length=5, max_length=1000, description="The text prompt for the chat"
9
+ )
10
+ format: Optional[str] = Field(
11
+ None, description="The format of the response, e.g., text, markdown"
12
+ )
13
+ tone: Optional[str] = Field(
14
+ None, description="The tone of the response, e.g., formal, informal"
15
+ )
16
+ target_audience: Optional[str] = Field(
17
+ None, description="The target audience for the response"
18
+ )
19
+
20
+
21
+ class ChatOutput(BaseModel):
22
+ result: str = Field(..., description="The transformed text response")
src/expressly_server/tools/__init__.py ADDED
File without changes
src/expressly_server/utils/utils.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+
4
+ def load_json_data(file_name: str, file_path: str) -> dict:
5
+ """
6
+ Loads JSON data from a file.
7
+
8
+ Args:
9
+ file_name (str): The name of the JSON file.
10
+ file_path (str): The path to the directory containing the file.
11
+
12
+ Returns:
13
+ dict: The content of the JSON file as a dictionary.
14
+ """
15
+
16
+ file_path = f"{file_path}/{file_name}"
17
+ with open(file_path, "r") as file:
18
+ return json.load(file)
19
+
20
+
21
+ def sanitize_input(input: str) -> str:
22
+ """
23
+ Sanitizes a string by stripping, lower casing, and replacing whitespace and hyphens with underscores.
24
+
25
+ Args:
26
+ input (str): The string to be sanitized.
27
+
28
+ Returns:
29
+ str: The sanitized string.
30
+ """
31
+ if input is None or input == "":
32
+ return ""
33
+ return input.strip().lower().replace(" ", "_").replace("-", "_")
src/expressly_server/web_app.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ from expressly_server.crew import ExpresslyServer
4
+
5
+
6
+ def call(prompt, target_audience, format, tone, active_tab):
7
+ """
8
+ Calls the Expressly Server API to generate content based on the given inputs.
9
+
10
+ Args:
11
+ prompt (str): The text prompt for the chat.
12
+ target_audience (str): The target audience for the response.
13
+ format (str): The format of the response, e.g., text, markdown.
14
+ tone (str): The tone of the response, e.g., formal, informal.
15
+ active_tab (str): The active tab on the UI, either "target_audience" or "format_tone".
16
+
17
+ Returns:
18
+ str: The generated text response.
19
+
20
+ Raises:
21
+ ValueError: If prompt is empty, or if the active_tab value is invalid.
22
+ """
23
+
24
+ # Validating and constructing the inputs
25
+ if prompt is None or prompt == "":
26
+ raise ValueError("Prompt is required")
27
+
28
+ if active_tab == "target_audience":
29
+ format = ""
30
+ tone = ""
31
+ elif active_tab == "format_tone":
32
+ target_audience = ""
33
+ else:
34
+ raise ValueError("Invalid active_tab value")
35
+
36
+ inputs = {
37
+ "prompt": prompt,
38
+ "target_audience": target_audience,
39
+ "format": format,
40
+ "tone": tone,
41
+ }
42
+
43
+ outputs = ExpresslyServer().crew().kickoff(inputs=inputs)
44
+
45
+ if outputs is None:
46
+ result = "Please check the inputs and try again. If the issue persists, contact support."
47
+ else:
48
+ result = outputs.raw
49
+
50
+ return result
51
+
52
+
53
+ with gr.Blocks() as app:
54
+ gr.Markdown("# Expressly - Text Transformation App")
55
+
56
+ with gr.Row():
57
+ # Left Column for Inputs
58
+ with gr.Column(scale=1):
59
+ prompt = gr.Textbox(label="Message Expressly", max_length=1024, lines=3)
60
+
61
+ # Create a state variable to store active tab
62
+ active_tab = gr.State("target_audience")
63
+
64
+ with gr.Tab("Target Audience", id="tab_audience") as tab1:
65
+ target_audience = gr.Dropdown(
66
+ [
67
+ "LinkedIn Post",
68
+ "WhatsApp Message",
69
+ "Tweet",
70
+ "News Article",
71
+ "Technical Blog",
72
+ "Formal Email",
73
+ "Instagram Post",
74
+ "Website Content",
75
+ "Marketing Email",
76
+ "Job Application",
77
+ "Customer Support Response",
78
+ ],
79
+ label="Target Audience",
80
+ info="Pick a Target Audience to specify the purpose or platform.",
81
+ )
82
+ # Update state when this tab is selected
83
+ tab1.select(lambda: "target_audience", None, active_tab)
84
+
85
+ with gr.Tab("Format & Tone", id="tab_format") as tab2:
86
+ format = gr.Dropdown(
87
+ [
88
+ "Post",
89
+ "Chat",
90
+ "Tweet",
91
+ "Email",
92
+ "Blog",
93
+ "Article",
94
+ "Report",
95
+ "Product Description",
96
+ ],
97
+ label="Format",
98
+ info="Choose a Format to define the type of content.",
99
+ )
100
+ tone = gr.Dropdown(
101
+ [
102
+ "Professional",
103
+ "Casual",
104
+ "Straightforward",
105
+ "Confident",
106
+ "Friendly",
107
+ "Neutral",
108
+ "Storytelling",
109
+ "Inspirational",
110
+ ],
111
+ label="Tone",
112
+ info="Select a Tone to set the communication style.",
113
+ )
114
+ # Update state when this tab is selected
115
+ tab2.select(lambda: "format_tone", None, active_tab)
116
+
117
+ btn_submit = gr.Button("Submit")
118
+
119
+ # Right Column for Output
120
+ with gr.Column(scale=1):
121
+ results = gr.Markdown(label="Result")
122
+
123
+ btn_submit.click(
124
+ fn=call,
125
+ inputs=[prompt, target_audience, format, tone, active_tab],
126
+ outputs=[results],
127
+ )
128
+
129
+
130
+ def launch():
131
+ """
132
+ Launch the Expressly web app.
133
+
134
+ This function starts the Expressly web app in a threaded mode, allowing it
135
+ to process multiple requests concurrently. The app is configured to
136
+ accept up to 10 requests in its queue at any given time.
137
+
138
+ The app is launched with strict CORS checks disabled, which allows it
139
+ to be accessed from any origin.
140
+
141
+ To launch the app, call this function with no arguments.
142
+
143
+ Example:
144
+ launch()
145
+ """
146
+ app.queue(max_size=10).launch(strict_cors=False)
147
+
148
+
149
+ if __name__ == "__main__":
150
+ launch()
uv.lock ADDED
The diff for this file is too large to render. See raw diff