iamspruce commited on
Commit
71192d1
·
1 Parent(s): f24a55f

updated fastapi

Browse files
Files changed (50) hide show
  1. .dockerignore +25 -0
  2. Dockerfile +8 -23
  3. app/core/app.py +31 -0
  4. app/core/config.py +9 -0
  5. app/core/logging.py +8 -0
  6. app/core/middleware.py +21 -0
  7. app/core/prompts.py +30 -0
  8. app/core/security.py +8 -22
  9. app/data/en/ablist.yml +697 -0
  10. app/data/en/condescending.yml +50 -0
  11. app/data/en/gender.yml +1808 -0
  12. app/data/en/lgbtq.yml +206 -0
  13. app/data/en/misc.yml +19 -0
  14. app/data/en/press.yml +20 -0
  15. app/data/en/race.yml +370 -0
  16. app/data/en/slogans.yml +9 -0
  17. app/data/en/suicide.yml +51 -0
  18. app/main.py +4 -47
  19. app/models.py +0 -108
  20. app/prompts.py +0 -121
  21. app/routers/conciseness.py +0 -42
  22. app/routers/grammar.py +52 -53
  23. app/routers/inclusive_language.py +8 -39
  24. app/routers/paraphrase.py +9 -36
  25. app/routers/punctuation.py +0 -67
  26. app/routers/readability.py +24 -46
  27. app/routers/rewrite.py +18 -0
  28. app/routers/sentence_correctness.py +0 -62
  29. app/routers/summarize.py +0 -39
  30. app/routers/tone.py +8 -53
  31. app/routers/translate.py +12 -38
  32. app/routers/vocabulary.py +0 -42
  33. app/routers/voice.py +8 -69
  34. app/schemas/base.py +13 -0
  35. app/services/__init__.py +0 -0
  36. app/services/base.py +42 -0
  37. app/services/conciseness_suggestion.py +30 -0
  38. app/services/gpt4_rewrite.py +44 -0
  39. app/services/grammar.py +32 -0
  40. app/services/inclusive_language.py +98 -0
  41. app/services/paraphrase.py +39 -0
  42. app/services/tone_classification.py +36 -0
  43. app/services/translation.py +40 -0
  44. app/services/vocabulary_enhancement.py +30 -0
  45. app/services/voice_detection.py +33 -0
  46. app/test/test_rewrite.py +15 -0
  47. app/test_main.py +0 -39
  48. app/utils/shared.py +4 -0
  49. requirements.txt +1 -1
  50. run.py +15 -0
.dockerignore ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python specific files and directories
2
+ *.pyc
3
+ *.pyo
4
+ __pycache__/
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .venv/
8
+ venv/
9
+ env/
10
+
11
+ # Editor/IDE specific files
12
+ .vscode/
13
+ .idea/
14
+ *.sublime-project
15
+ *.sublime-workspace
16
+
17
+ # Common build/distribution files
18
+ dist/
19
+ build/
20
+ *.egg-info/
21
+ .DS_Store
22
+
23
+ # Git specific files
24
+ .git/
25
+ .gitignore
Dockerfile CHANGED
@@ -1,43 +1,28 @@
1
  FROM python:3.10-slim
2
 
3
- # Set working directory inside the container
4
  WORKDIR /app
5
 
6
  # Install system dependencies
7
- # git is included for any potential future needs or if any dependency requires it for cloning
8
- # Install Java Runtime Environment (JRE) - crucial for language-tool-python
9
- RUN apt-get update && apt-get install -y git default-jre && rm -rf /var/lib/apt/lists/*
10
 
11
- # Install Python dependencies from requirements.txt
12
- # Ensure requirements.txt is copied before installing to leverage Docker cache
13
  COPY requirements.txt .
14
  RUN pip install --no-cache-dir -r requirements.txt
15
 
16
- # Download the spaCy English language model
17
- # This is crucial for resolving the "[E050] Can't find model 'en_core_web_sm'" error
18
  RUN python -m spacy download en_core_web_sm
19
 
20
- # Setup Hugging Face cache directory and permissions
21
- # This directory will also be used by language-tool-python for its main downloads.
22
  ENV HF_HOME=/cache
23
  RUN mkdir -p /cache && chmod -R 777 /cache
24
 
25
- # ... other Dockerfile content ...
26
- ENV GRAMMAFREE_API_KEY="admin"
27
 
28
- # Explicitly create and set permissions for /.cache
29
- # This is to address PermissionError: [Errno 13] Permission denied: '/.cache'
30
- # which language-tool-python might be trying to write to.
31
  RUN mkdir -p /.cache && chmod -R 777 /.cache
32
 
33
- # Set environment variable for language-tool-python download directory
34
- # This redirects LanguageTool's primary downloads to the shared /cache directory.
35
- ENV LANGUAGE_TOOL_DOWNLOAD_DIR=/cache
36
 
37
- # Copy the entire application code into the container
38
- # This copies your 'app' directory, including main.py, routers, models, etc.
39
  COPY app ./app
40
 
41
- # Command to run the FastAPI application using Uvicorn
42
- # Binds the app to all network interfaces (0.0.0.0) on port 7860
43
- CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
1
  FROM python:3.10-slim
2
 
 
3
  WORKDIR /app
4
 
5
  # Install system dependencies
6
+ RUN apt-get update && apt-get install -y git && \
7
+ rm -rf /var/lib/apt/lists/*
 
8
 
 
 
9
  COPY requirements.txt .
10
  RUN pip install --no-cache-dir -r requirements.txt
11
 
12
+
 
13
  RUN python -m spacy download en_core_web_sm
14
 
15
+
 
16
  ENV HF_HOME=/cache
17
  RUN mkdir -p /cache && chmod -R 777 /cache
18
 
 
 
19
 
 
 
 
20
  RUN mkdir -p /.cache && chmod -R 777 /.cache
21
 
 
 
 
22
 
 
 
23
  COPY app ./app
24
 
25
+ # Expose the port your FastAPI application will run on
26
+ EXPOSE 7860
27
+
28
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
app/core/app.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from fastapi.middleware.gzip import GZipMiddleware
3
+ from app.routers import (
4
+ grammar,
5
+ tone,
6
+ voice,
7
+ inclusive_language,
8
+ readability,
9
+ paraphrase,
10
+ translate,
11
+ rewrite,
12
+ )
13
+
14
+ def create_app() -> FastAPI:
15
+ app = FastAPI()
16
+ app.add_middleware(GZipMiddleware, minimum_size=500)
17
+
18
+ app.include_router(grammar.router)
19
+ app.include_router(tone.router)
20
+ app.include_router(voice.router)
21
+ app.include_router(inclusive_language.router)
22
+ app.include_router(readability.router)
23
+ app.include_router(paraphrase.router)
24
+ app.include_router(translate.router)
25
+ app.include_router(rewrite.router)
26
+
27
+ @app.get("/")
28
+ def root():
29
+ return {"message": "Welcome to Grammafree API"}
30
+
31
+ return app
app/core/config.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic_settings import BaseSettings
2
+
3
+ class Settings(BaseSettings):
4
+ inclusive_rules_dir: str = "app/data/en"
5
+ openai_model: str = "gpt-4o"
6
+ openai_temperature: float = 0.7
7
+ openai_max_tokens: int = 512
8
+
9
+ settings = Settings()
app/core/logging.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # core/logging.py
2
+ import logging
3
+
4
+ def configure_logging():
5
+ logging.basicConfig(
6
+ level=logging.INFO,
7
+ format="%(asctime)s - %(levelname)s - %(message)s"
8
+ )
app/core/middleware.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request
2
+ from fastapi.responses import JSONResponse
3
+ from slowapi import Limiter
4
+ from slowapi.util import get_remote_address
5
+ from slowapi.errors import RateLimitExceeded
6
+ import logging
7
+
8
+ limiter = Limiter(key_func=get_remote_address)
9
+
10
+ def setup_middlewares(app: FastAPI):
11
+ @app.exception_handler(Exception)
12
+ async def unhandled_exception_handler(request: Request, exc: Exception):
13
+ logging.exception("Unhandled exception")
14
+ return JSONResponse(status_code=500, content={"detail": "Internal server error"})
15
+
16
+ @app.exception_handler(RateLimitExceeded)
17
+ async def rate_limit_exceeded_handler(request: Request, exc: RateLimitExceeded):
18
+ return JSONResponse(status_code=429, content={"detail": "Rate limit exceeded"})
19
+
20
+ limiter.init_app(app)
21
+ app.state.limiter = limiter
app/core/prompts.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def tone_prompt(text: str, tone: str) -> str:
2
+ return f"Change the tone of this sentence to {tone}: {text.strip()}"
3
+
4
+ def summarize_prompt(text: str) -> str:
5
+ return f"Summarize the following text:\n{text.strip()}"
6
+
7
+ def clarity_prompt(text: str) -> str:
8
+ return f"Improve the clarity of the following sentence:\n{text.strip()}"
9
+
10
+ def conciseness_prompt(text: str) -> str:
11
+ return f"Make the following sentence more concise:\n{text.strip()}"
12
+
13
+ def rewrite_prompt(text: str, instruction: str) -> str:
14
+ return f"{instruction.strip()}\n{text.strip()}"
15
+
16
+ def vocabulary_prompt(text: str) -> str:
17
+ return (
18
+ "You are an expert vocabulary enhancer. Rewrite the following text "
19
+ "by replacing common and simple words with more sophisticated, "
20
+ "precise, and contextually appropriate synonyms. Do not change "
21
+ "the original meaning. Maintain the tone.\n" + text.strip()
22
+ )
23
+
24
+ def concise_prompt(text: str) -> str:
25
+ return (
26
+ "You are an expert editor specializing in conciseness. "
27
+ "Rewrite the following text to be more concise and to the point, "
28
+ "removing any verbose phrases, redundant words, or unnecessary clauses. "
29
+ "Maintain the original meaning and professional tone.\n" + text.strip()
30
+ )
app/core/security.py CHANGED
@@ -1,25 +1,11 @@
1
- from fastapi import Header, HTTPException
2
  import os
 
3
 
4
- # Define a simple API key for authentication.
5
- # In a production environment, this should be a more robust and securely managed key.
6
- # Load API_KEY from an environment variable for security.
7
- # Fallback to a default for local development if not set, but warn about it.
8
- API_KEY = os.getenv("GRAMMAFREE_API_KEY", "12345") # Use a strong default for production!
9
 
10
- if API_KEY == "12345":
11
- print("WARNING: Using default API_KEY. Set GRAMMAFREE_API_KEY environment variable in production!")
12
-
13
- def verify_api_key(x_api_key: str = Header(...)):
14
- """
15
- Dependency function to verify the API key provided in the request headers.
16
-
17
- Args:
18
- x_api_key (str): The API key expected in the 'X-API-Key' header.
19
-
20
- Raises:
21
- HTTPException: If the provided API key does not match the expected API_KEY.
22
- """
23
- if x_api_key != API_KEY:
24
- # Raise an HTTPException with 401 Unauthorized status if the key is invalid.
25
- raise HTTPException(status_code=401, detail="Unauthorized")
 
 
1
  import os
2
+ from fastapi import Header, HTTPException, status, Depends
3
 
4
+ API_KEY = os.getenv("GRAMMAFREE_API_KEY", "12345")
 
 
 
 
5
 
6
+ def verify_api_key(x_api_key: str = Header(...)) -> None:
7
+ if not x_api_key or x_api_key != API_KEY:
8
+ raise HTTPException(
9
+ status_code=status.HTTP_401_UNAUTHORIZED,
10
+ detail="Invalid or missing API key"
11
+ )
 
 
 
 
 
 
 
 
 
 
app/data/en/ablist.yml ADDED
@@ -0,0 +1,697 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - type: basic
2
+ note: Refer to the person, rather than the disability, first.
3
+ considerate: person with learning disabilities
4
+ inconsiderate: learning disabled
5
+ - type: basic
6
+ note: Refer to the person, rather than the disability, first.
7
+ considerate:
8
+ - turned off
9
+ - has a disability
10
+ - person with a disability
11
+ - people with disabilities
12
+ inconsiderate:
13
+ - disabled
14
+ - invalid
15
+ - type: basic
16
+ source: https://ncdj.org/style-guide/
17
+ note: Assumes/implies that a person with a disability is deficient or inferior to others. When possible, specify the functional ability or its restriction.
18
+ considerate:
19
+ - has a disability
20
+ - person with a disability
21
+ - people with disabilities
22
+ inconsiderate:
23
+ - birth defect
24
+ - type: basic
25
+ source: https://ncdj.org/style-guide/
26
+ note: Assumes that a person with a disability has a reduced quality of life.
27
+ considerate:
28
+ - has a disability
29
+ - person with a disability
30
+ - people with disabilities
31
+ inconsiderate:
32
+ - suffers from disabilities
33
+ - suffering from disabilities
34
+ - suffering from a disability
35
+ - afflicted with disabilities
36
+ - afflicted with a disability
37
+ - type: basic
38
+ source: https://ncdj.org/style-guide/
39
+ note: Refer to the person, rather than the disability, first.
40
+ considerate:
41
+ - people with intellectual disabilities
42
+ inconsiderate:
43
+ - intellectually disabled people
44
+ - type: basic
45
+ source: https://ncdj.org/style-guide/
46
+ note: Assumes that a person with an intellectual disability has a reduced quality of life.
47
+ considerate:
48
+ - person with an intellectual disability
49
+ inconsiderate:
50
+ - intellectually disabled
51
+ - has intellectual issues
52
+ - suffers from intellectual disabilities
53
+ - suffering from intellectual disabilities
54
+ - suffering from an intellectual disability
55
+ - afflicted with intellectual disabilities
56
+ - afflicted with a intellectual disability
57
+ - type: basic
58
+ source: https://ncdj.org/style-guide/
59
+ note: Describe the behavior or illness without derogatory words.
60
+ considerate:
61
+ - rude
62
+ - malicious
63
+ - mean
64
+ - disgusting
65
+ - incredible
66
+ - vile
67
+ - person with symptoms of mental illness
68
+ - person with mental illness
69
+ - person with symptoms of a mental disorder
70
+ - person with a mental disorder
71
+ inconsiderate:
72
+ - batshit
73
+ - psycho
74
+ - crazy
75
+ - delirious
76
+ - insane
77
+ - insanity
78
+ - loony
79
+ - lunacy
80
+ - lunatic
81
+ - mentally ill
82
+ - psychopathology
83
+ - mental defective
84
+ - moron
85
+ - moronic
86
+ - nuts
87
+ - mental case
88
+ - mental
89
+ - type: basic
90
+ note: Describe the behavior or illness without derogatory words.
91
+ considerate:
92
+ - incredibly
93
+ inconsiderate:
94
+ - insanely
95
+ - type: basic
96
+ note: When describing a mathematical or programmatic value, using the word “sane” needlessly invokes the topic of mental health. Consider using a domain-specific or neutral term instead.
97
+ considerate:
98
+ - correct
99
+ - adequate
100
+ - sufficient
101
+ - consistent
102
+ - valid
103
+ - coherent
104
+ - sensible
105
+ - reasonable
106
+ inconsiderate: sane
107
+ - type: basic
108
+ note: When describing a mathematical or programmatic value, using the phrase “sanity check” needlessly invokes the topic of mental health. Consider using simply “check”, or a domain-specific or neutral term, instead.
109
+ considerate:
110
+ - check
111
+ - assertion
112
+ - validation
113
+ - smoke test
114
+ inconsiderate: sanity check
115
+ - type: basic
116
+ source: https://ncdj.org/style-guide/
117
+ note: Only use terms describing mental illness when referring to a professionally diagnosed medical condition.
118
+ considerate:
119
+ - fluctuating
120
+ - person with bipolar disorder
121
+ inconsiderate:
122
+ - bipolar
123
+ - type: basic
124
+ source: https://ncdj.org/style-guide/
125
+ note: Only use terms describing mental illness when referring to a professionally diagnosed medical condition.
126
+ considerate:
127
+ - person with schizophrenia
128
+ inconsiderate:
129
+ - schizophrenic
130
+ - schizo
131
+ - type: basic
132
+ source: https://ncdj.org/style-guide/
133
+ note: Assumes a person with schizophrenia experiences a reduced quality of life.
134
+ considerate:
135
+ - person with schizophrenia
136
+ inconsiderate:
137
+ - suffers from schizophrenia
138
+ - suffering from schizophrenia
139
+ - afflicted with schizophrenia
140
+ - manic
141
+ - type: basic
142
+ source: https://ncdj.org/style-guide/
143
+ considerate: accessible parking
144
+ inconsiderate: handicapped parking
145
+ - type: basic
146
+ source: https://ncdj.org/style-guide/
147
+ note: Refer to the person, rather than the disability, first.
148
+ considerate:
149
+ - person with a handicap
150
+ - accessible
151
+ inconsiderate: handicapped
152
+ - type: basic
153
+ source: https://ncdj.org/style-guide/
154
+ note: Refer to the person, rather than the condition, first.
155
+ considerate: person with an amputation
156
+ inconsiderate: amputee
157
+ - type: basic
158
+ note: Refer to the specific disability.
159
+ considerate: person with a limp
160
+ inconsiderate:
161
+ - cripple
162
+ - crippled
163
+ - gimp
164
+ - type: basic
165
+ considerate: person with Down Syndrome
166
+ inconsiderate: mongoloid
167
+ - type: basic
168
+ note: Refer to the person, rather than the condition, first.
169
+ considerate: individual who has had a stroke
170
+ inconsiderate:
171
+ - stroke victim
172
+ - suffering from a stroke
173
+ - victim of a stroke
174
+ - type: basic
175
+ considerate: person who has multiple sclerosis
176
+ inconsiderate:
177
+ - suffers from multiple sclerosis
178
+ - suffering from multiple sclerosis
179
+ - victim of multiple sclerosis
180
+ - multiple sclerosis victim
181
+ - afflicted with multiple sclerosis
182
+ - type: basic
183
+ source: https://ncdj.org/style-guide
184
+ note: Refer to a person's condition as a state, not as an affliction.
185
+ considerate: person who has muscular dystrophy
186
+ inconsiderate:
187
+ - suffers from muscular dystrophy
188
+ - afflicted with muscular dystrophy
189
+ - suffers from MD
190
+ - afflicted with MD
191
+ - type: basic
192
+ considerate: with family support needs
193
+ inconsiderate: family burden
194
+ - type: basic
195
+ considerate:
196
+ - psychiatric hospital
197
+ - mental health hospital
198
+ inconsiderate:
199
+ - asylum
200
+ - type: basic
201
+ considerate:
202
+ - chaos
203
+ - hectic
204
+ - pandemonium
205
+ inconsiderate:
206
+ - bedlam
207
+ - madhouse
208
+ - loony bin
209
+ - type: basic
210
+ source: https://media.specialolympics.org/soi/files/press-kit/2014_FactSheet_Final.pdf
211
+ considerate:
212
+ - Down Syndrome
213
+ inconsiderate:
214
+ - downs syndrome
215
+ - type: basic
216
+ considerate:
217
+ - silly
218
+ - dullard
219
+ - person with Down Syndrome
220
+ - person with developmental disabilities
221
+ - delay
222
+ - hold back
223
+ inconsiderate:
224
+ - retard
225
+ - retarded
226
+ - short bus
227
+ - type: basic
228
+ considerate:
229
+ - sillies
230
+ - dullards
231
+ - people with developmental disabilities
232
+ - people with Down’s Syndrome
233
+ - delays
234
+ - holds back
235
+ inconsiderate: retards
236
+ - type: basic
237
+ note: Only use terms describing mental illness when referring to a professionally diagnosed medical condition.
238
+ considerate:
239
+ - person with a psychotic condition
240
+ - person with psychosis
241
+ inconsiderate:
242
+ - psychotic
243
+ - suffers from psychosis
244
+ - suffering from psychosis
245
+ - afflicted with psychosis
246
+ - victim of psychosis
247
+ - type: basic
248
+ source: https://ncdj.org/style-guide/
249
+ considerate:
250
+ - boring
251
+ - dull
252
+ inconsiderate: lame
253
+ - type: basic
254
+ considerate:
255
+ - person with AIDS
256
+ inconsiderate:
257
+ - suffering from aids
258
+ - suffer from aids
259
+ - suffers from aids
260
+ - afflicted with aids
261
+ - victim of aids
262
+ - aids victim
263
+ - type: basic
264
+ considerate:
265
+ - uses a wheelchair
266
+ inconsiderate:
267
+ - confined to a wheelchair
268
+ - bound to a wheelchair
269
+ - restricted to a wheelchair
270
+ - wheelchair bound
271
+ - type: basic
272
+ source: https://media.specialolympics.org/soi/files/press-kit/2014_FactSheet_Final.pdf
273
+ note: When possible, use the exact discipline of sport.
274
+ considerate:
275
+ - athletes
276
+ - Special Olympics athletes
277
+ inconsiderate:
278
+ - special olympians
279
+ - special olympic athletes
280
+ - type: basic
281
+ source: https://ncdj.org/style-guide/
282
+ note: Can imply that people with disabilities lack the ability to use their bodies well. Sometimes `typical` can be used.
283
+ considerate:
284
+ - non-disabled
285
+ inconsiderate:
286
+ - ablebodied
287
+ - type: basic
288
+ source: https://ncdj.org/style-guide/
289
+ note: Addiction is a neurobiological disease.
290
+ considerate:
291
+ - person with a drug addiction
292
+ - person recovering from a drug addiction
293
+ inconsiderate:
294
+ - addict
295
+ - junkie
296
+ - type: basic
297
+ source: https://ncdj.org/style-guide/
298
+ note: Addiction is a neurobiological disease.
299
+ considerate:
300
+ - people with a drug addiction
301
+ - people recovering from a drug addiction
302
+ inconsiderate:
303
+ - addicts
304
+ - junkies
305
+ - type: basic
306
+ source: https://ncdj.org/style-guide/
307
+ note: Alcoholism is a neurobiological disease.
308
+ considerate:
309
+ - someone with an alcohol problem
310
+ inconsiderate:
311
+ - alcoholic
312
+ - alcohol abuser
313
+ - type: basic
314
+ source: https://ncdj.org/style-guide/
315
+ considerate:
316
+ - deaf
317
+ inconsiderate:
318
+ - deaf and dumb
319
+ - deafmute
320
+ - type: basic
321
+ source: https://ncdj.org/style-guide/
322
+ considerate:
323
+ - person with dementia
324
+ inconsiderate:
325
+ - demented
326
+ - senile
327
+ - type: basic
328
+ source: https://ncdj.org/style-guide/
329
+ considerate:
330
+ - sad
331
+ - blue
332
+ - bummed out
333
+ - person with seasonal affective disorder
334
+ - person with psychotic depression
335
+ - person with postpartum depression
336
+ inconsiderate:
337
+ - depressed
338
+ - type: basic
339
+ source:
340
+ - https://ncdj.org/style-guide/
341
+ - https://www.lpaonline.org/faq-#Midget
342
+ considerate:
343
+ - person with dwarfism
344
+ - little person
345
+ - little people
346
+ - LP
347
+ - person of short stature
348
+ inconsiderate:
349
+ - vertically challenged
350
+ - midget
351
+ - small person
352
+ - dwarf
353
+ - type: basic
354
+ source: https://ncdj.org/style-guide/
355
+ considerate:
356
+ - person with dyslexia
357
+ inconsiderate:
358
+ - dyslexic
359
+ - type: basic
360
+ source: https://ncdj.org/style-guide/
361
+ considerate:
362
+ - person with epilepsy
363
+ inconsiderate:
364
+ - epileptic
365
+ - type: basic
366
+ source: https://ncdj.org/style-guide/
367
+ note: When possible, ask the person what they prefer.
368
+ considerate:
369
+ - hard of hearing
370
+ - partially deaf
371
+ - partial hearing loss
372
+ - deaf
373
+ inconsiderate:
374
+ - hearing impaired
375
+ - hearing impairment
376
+ - type: basic
377
+ source: https://ncdj.org/style-guide/
378
+ considerate:
379
+ - polio
380
+ - person who had polio
381
+ inconsiderate:
382
+ - infantile paralysis
383
+ - suffers from polio
384
+ - suffering from polio
385
+ - suffering from a polio
386
+ - afflicted with polio
387
+ - afflicted with a polio
388
+ - victim of polio
389
+ - type: basic
390
+ source: https://ncdj.org/style-guide/
391
+ considerate:
392
+ - sustain an injury
393
+ - receive an injury
394
+ inconsiderate:
395
+ - suffer from an injury
396
+ - suffers from an injury
397
+ - suffering from an injury
398
+ - afflicted with an injury
399
+ - victim of an injury
400
+ - type: basic
401
+ source: https://ncdj.org/style-guide/
402
+ considerate:
403
+ - sustain injuries
404
+ - receive injuries
405
+ inconsiderate:
406
+ - suffer from injuries
407
+ - suffers from injuries
408
+ - suffering from injuries
409
+ - afflicted with injuries
410
+ - victim of injuries
411
+ - type: basic
412
+ source: https://ncdj.org/style-guide/
413
+ considerate:
414
+ - person with paraplegia
415
+ inconsiderate:
416
+ - paraplegic
417
+ - type: basic
418
+ source: https://ncdj.org/style-guide/
419
+ considerate:
420
+ - person with quadriplegia
421
+ inconsiderate:
422
+ - quadriplegic
423
+ - type: basic
424
+ source: https://ncdj.org/style-guide/
425
+ considerate:
426
+ - person with cerebral palsy
427
+ - twitch
428
+ - flinch
429
+ - hectic
430
+ inconsiderate:
431
+ - spaz
432
+ - type: basic
433
+ source: https://ncdj.org/style-guide/
434
+ considerate:
435
+ - person with cerebral palsy
436
+ - twitch
437
+ - flinch
438
+ inconsiderate:
439
+ - spastic
440
+ - type: basic
441
+ source: https://ncdj.org/style-guide/
442
+ considerate:
443
+ - stuttering
444
+ - disfluency of speech
445
+ inconsiderate:
446
+ - stammering
447
+ - type: basic
448
+ source: https://ncdj.org/style-guide/
449
+ considerate:
450
+ - person who stutters
451
+ inconsiderate:
452
+ - stutterer
453
+ - type: basic
454
+ source: https://ncdj.org/style-guide/
455
+ considerate:
456
+ - Tourette syndrome
457
+ inconsiderate:
458
+ - tourettes syndrome
459
+ - tourettes disorder
460
+ - type: basic
461
+ source: https://ncdj.org/style-guide/
462
+ considerate:
463
+ - treatment center
464
+ inconsiderate:
465
+ - rehab center
466
+ - detox center
467
+ - type: basic
468
+ source: https://ncdj.org/style-guide/
469
+ considerate:
470
+ - treatment
471
+ inconsiderate:
472
+ - rehab
473
+ - detox
474
+ - type: basic
475
+ source: https://ncdj.org/style-guide/
476
+ note: Only use terms describing mental illness when referring to a professionally diagnosed medical condition.
477
+ considerate:
478
+ - person with a personality disorder
479
+ - person with psychopathic personality
480
+ inconsiderate:
481
+ - sociopath
482
+ - type: basic
483
+ source: https://ncdj.org/style-guide/
484
+ note: Only use terms describing mental illness when referring to a professionally diagnosed medical condition.
485
+ considerate:
486
+ - people with psychopathic personalities
487
+ - people with a personality disorder
488
+ inconsiderate:
489
+ - sociopaths
490
+ - type: basic
491
+ source: https://www.autistichoya.com/p/ableist-words-and-terms-to-avoid.html
492
+ note: Dumb here is used in 2 different contexts , the inability to talk or as a curse word.
493
+ considerate:
494
+ - foolish
495
+ - ludicrous
496
+ - speechless
497
+ - silent
498
+ inconsiderate:
499
+ - dumb
500
+ - type: basic
501
+ source: https://web.archive.org/web/20181025174508/http://www.mmonjejr.com:80/2014/02/deconstructing-stupid.html
502
+ considerate:
503
+ - foolish
504
+ - ludicrous
505
+ - unintelligent
506
+ inconsiderate:
507
+ - simpleton
508
+ - stupid
509
+ - wacko
510
+ - whacko
511
+ - low iq
512
+ - type: basic
513
+ considerate:
514
+ - fit of terror
515
+ - scare
516
+ inconsiderate:
517
+ - panic attack
518
+ - type: basic
519
+ considerate:
520
+ - thin
521
+ - slim
522
+ inconsiderate:
523
+ - anorexic
524
+ - bony
525
+ - type: basic
526
+ source: https://english.stackexchange.com/questions/247550/
527
+ note: Only use terms describing mental illness when referring to a professionally diagnosed medical condition.
528
+ considerate:
529
+ - has an anxiety disorder
530
+ - obsessive
531
+ - pedantic
532
+ - niggly
533
+ - picky
534
+ inconsiderate:
535
+ - neurotic
536
+ - ocd
537
+ - o.c.d
538
+ - o.c.d.
539
+ - type: basic
540
+ considerate:
541
+ - restlessness
542
+ - sleeplessness
543
+ inconsiderate:
544
+ - insomnia
545
+ - type: basic
546
+ considerate:
547
+ - person who has insomnia
548
+ inconsiderate:
549
+ - insomniac
550
+ - type: basic
551
+ considerate:
552
+ - people who have insomnia
553
+ inconsiderate:
554
+ - insomniacs
555
+ - type: basic
556
+ source: https://www.autistichoya.com/p/ableist-words-and-terms-to-avoid.html
557
+ considerate:
558
+ - empty
559
+ - sterile
560
+ - infertile
561
+ inconsiderate:
562
+ - barren
563
+ - type: basic
564
+ source: https://www.autistichoya.com/p/ableist-words-and-terms-to-avoid.html
565
+ considerate:
566
+ - careless
567
+ - heartless
568
+ - indifferent
569
+ - insensitive
570
+ inconsiderate:
571
+ - blind to
572
+ - blind eye to
573
+ - blinded by
574
+ - deaf to
575
+ - deaf ear to
576
+ - deafened by
577
+ - type: basic
578
+ source: https://www.autistichoya.com/p/ableist-words-and-terms-to-avoid.html
579
+ considerate:
580
+ - creep
581
+ - fool
582
+ inconsiderate:
583
+ - cretin
584
+ - type: basic
585
+ source: https://www.autistichoya.com/p/ableist-words-and-terms-to-avoid.html
586
+ considerate:
587
+ - absurd
588
+ - foolish
589
+ inconsiderate:
590
+ - daft
591
+ - type: basic
592
+ source: https://www.autistichoya.com/p/ableist-words-and-terms-to-avoid.html
593
+ considerate:
594
+ - foolish
595
+ - ludicrous
596
+ - silly
597
+ inconsiderate:
598
+ - feebleminded
599
+ - feeble minded
600
+ - idiot
601
+ - imbecile
602
+ - type: basic
603
+ source: https://www.autistichoya.com/p/ableist-words-and-terms-to-avoid.html
604
+ note: Sometimes it's cleft lip or palate, not both. Specify when possible.
605
+ considerate:
606
+ - person with a cleft-lip and palate
607
+ inconsiderate:
608
+ - harelipped
609
+ - cleftlipped
610
+ - type: basic
611
+ source: https://www.autistichoya.com/p/ableist-words-and-terms-to-avoid.html
612
+ considerate:
613
+ - cleft-lip and palate
614
+ inconsiderate:
615
+ - harelip
616
+ - hare lip
617
+ - type: basic
618
+ source: https://www.autistichoya.com/p/ableist-words-and-terms-to-avoid.html
619
+ considerate:
620
+ - fanatic
621
+ - zealot
622
+ - enthusiast
623
+ inconsiderate:
624
+ - maniac
625
+ - type: basic
626
+ considerate:
627
+ - person with prominent teeth
628
+ - prominent teeth
629
+ inconsiderate:
630
+ - bucktoothed
631
+ - buckteeth
632
+ - type: basic
633
+ source:
634
+ - http://cdrnys.org/blog/disability-dialogue/the-disability-dialogue-4-disability-euphemisms-that-need-to-bite-the-dust/
635
+ - https://www.autistichoya.com/p/ableist-words-and-terms-to-avoid.html
636
+ note: Euphemisms for disabilities can be infantilizing.
637
+ considerate:
638
+ - has a disability
639
+ - person with a disability
640
+ - people with disabilities
641
+ inconsiderate:
642
+ - challenged
643
+ - diffability
644
+ - differently abled
645
+ - handicapable
646
+ - special
647
+ - special needs
648
+ - specially abled
649
+ - type: basic
650
+ source: https://www.autistichoya.com/p/ableist-words-and-terms-to-avoid.html
651
+ considerate:
652
+ - disagreeable
653
+ - uneducated
654
+ - ignorant
655
+ - naive
656
+ - inconsiderate
657
+ inconsiderate:
658
+ - fucktard
659
+ - libtard
660
+ - contard
661
+ - type: basic
662
+ considerate:
663
+ - disorganized
664
+ - distracted
665
+ - energetic
666
+ - hyperactive
667
+ - impetuous
668
+ - impulsive
669
+ - inattentive
670
+ - restless
671
+ - unfocused
672
+ inconsiderate:
673
+ # Upper case to only match `ADD`, not `add`.
674
+ - ADD
675
+ - adhd
676
+ - a.d.d.
677
+ - a.d.h.d.
678
+ - type: basic
679
+ note: Dummy can refer to the inability to talk or be used as a derogatory word meaning stupid. In computer programming it is used where a value or behavior is unimportant. There are better alternatives for other use cases also.
680
+ considerate:
681
+ - test double
682
+ - placeholder
683
+ - fake
684
+ - stub
685
+ inconsiderate:
686
+ - dummyvariable
687
+ - dummyvalue
688
+ - dummyobject
689
+ - dummy
690
+ - type: basic
691
+ note: Binge might be insensitive towards folks with eating or drinking disorders
692
+ source: https://github.com/retextjs/retext-equality/issues/110
693
+ considerate:
694
+ - enthusiastic
695
+ - spree
696
+ inconsiderate:
697
+ - binge
app/data/en/condescending.yml ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # List of words to avoid, specifically in education writing.
2
+ # From: https://css-tricks.com/words-avoid-educational-writing/
3
+ # The terms `however` and `so` are not included as they’re only an offense if at
4
+ # the start of a sentence according to CSS Tricks.
5
+ # If you want messages for those, use `retext-intensify`.
6
+ - type: basic
7
+ inconsiderate:
8
+ - obvious
9
+ - obviously
10
+ note: Not everything is as obvious as you might think. And if it isn’t obvious to the reader, it can hurt.
11
+ source: https://css-tricks.com/words-avoid-educational-writing/
12
+ - type: basic
13
+ inconsiderate: just
14
+ note: Not everything is as easy as you might think. And if it isn’t easy for the reader, it can hurt.
15
+ source: https://css-tricks.com/words-avoid-educational-writing/
16
+ - type: basic
17
+ inconsiderate: basically
18
+ note: It’s probably not that basic. If you’re going to explain a confusing previous sentence with a clearer next sentence, why not drop the former and only use the latter?
19
+ source: https://css-tricks.com/words-avoid-educational-writing/
20
+ - type: basic
21
+ inconsiderate:
22
+ - simple
23
+ - simply
24
+ note: It’s probably not that simple. Even if it is, you probably don’t need to specifically say it.
25
+ source: https://css-tricks.com/words-avoid-educational-writing/
26
+ - type: basic
27
+ inconsiderate:
28
+ - easy
29
+ - easily
30
+ note: It’s probably not that easy. Even if it is, you probably don’t need to specifically say it.
31
+ source: https://css-tricks.com/words-avoid-educational-writing/
32
+ - type: basic
33
+ inconsiderate: of course
34
+ note: If it’s self-evident then maybe you don’t need to describe it. If it isn’t, don’t say it.
35
+ source: https://css-tricks.com/words-avoid-educational-writing/
36
+ - type: basic
37
+ inconsiderate: clearly
38
+ note: If it’s self-evident then maybe you don’t need to describe it. If it isn’t, don’t say it.
39
+ source: https://css-tricks.com/words-avoid-educational-writing/
40
+ - type: basic
41
+ inconsiderate: everyone knows
42
+ note: If it’s self-evident then maybe you don’t need to describe it. If it isn’t, don’t say it.
43
+ source: https://css-tricks.com/words-avoid-educational-writing/
44
+ - type: basic
45
+ inconsiderate:
46
+ - straight forward
47
+ - straightforward
48
+ - straight forwardly
49
+ - straightforwardly
50
+ note: It’s probably not that straight forward. Even if it is, you probably don’t need to specifically say it.
app/data/en/gender.yml ADDED
@@ -0,0 +1,1808 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - type: or
2
+ condition: when referring to a person
3
+ considerate:
4
+ - their
5
+ - theirs
6
+ - them
7
+ inconsiderate:
8
+ her: female
9
+ hers: female
10
+ him: male
11
+ his: male
12
+ - type: or
13
+ apostrophe: true
14
+ considerate:
15
+ - they
16
+ - it
17
+ inconsiderate:
18
+ she: female
19
+ he: male
20
+ she'd: female
21
+ he'd: male
22
+ she'll: female
23
+ he'll: male
24
+ she's: female
25
+ he's: male
26
+ - type: or
27
+ considerate:
28
+ - themselves
29
+ - theirself
30
+ - self
31
+ inconsiderate:
32
+ herself: female
33
+ himself: male
34
+ - type: or
35
+ condition: when referring to a person
36
+ considerate:
37
+ - kid
38
+ - child
39
+ - youth
40
+ inconsiderate:
41
+ girl: female
42
+ boy: male
43
+ - type: or
44
+ considerate:
45
+ - people
46
+ - persons
47
+ - folks
48
+ inconsiderate:
49
+ women: female
50
+ girls: female
51
+ gals: female
52
+ ladies: female
53
+ man: male
54
+ boys: male
55
+ men: male
56
+ guys: male
57
+ dudes: male
58
+ gents: male
59
+ gentlemen: male
60
+ - type: or
61
+ considerate:
62
+ - person
63
+ - friend
64
+ - pal
65
+ - folk
66
+ - individual
67
+ inconsiderate:
68
+ woman: female
69
+ gal: female
70
+ lady: female
71
+ babe: female
72
+ bimbo: female
73
+ chick: female
74
+ guy: male
75
+ lad: male
76
+ fellow: male
77
+ dude: male
78
+ bro: male
79
+ gentleman: male
80
+ - type: or
81
+ considerate:
82
+ - native land
83
+ - homeland
84
+ inconsiderate:
85
+ motherland: female
86
+ fatherland: male
87
+ - type: or
88
+ considerate:
89
+ - native tongue
90
+ - native language
91
+ inconsiderate:
92
+ mother tongue: female
93
+ father tongue: male
94
+ - type: or
95
+ considerate:
96
+ - first-year students
97
+ - freshers
98
+ inconsiderate:
99
+ freshwomen: female
100
+ freshmen: male
101
+ - type: or
102
+ considerate:
103
+ - garbage collector
104
+ - waste collector
105
+ - trash collector
106
+ inconsiderate:
107
+ garbagewoman: female
108
+ garbageman: male
109
+ - type: or
110
+ considerate:
111
+ - garbage collectors
112
+ - waste collectors
113
+ - trash collectors
114
+ inconsiderate:
115
+ garbagewomen: female
116
+ garbagemen: male
117
+ - type: or
118
+ considerate:
119
+ - chair
120
+ - head
121
+ - chairperson
122
+ - coordinator
123
+ - committee head
124
+ - moderator
125
+ - presiding officer
126
+ inconsiderate:
127
+ chairwoman: female
128
+ chairman: male
129
+ - type: or
130
+ considerate:
131
+ - committee member
132
+ inconsiderate:
133
+ committee woman: female
134
+ committee man: male
135
+ - type: or
136
+ considerate:
137
+ - cowhand
138
+ inconsiderate:
139
+ cowgirl: female
140
+ cowboy: male
141
+ - type: or
142
+ considerate:
143
+ - cowhands
144
+ inconsiderate:
145
+ cowgirls: female
146
+ cowboys: male
147
+ - type: or
148
+ considerate:
149
+ - cattle rancher
150
+ inconsiderate:
151
+ cattlewoman: female
152
+ cattleman: male
153
+ - type: or
154
+ considerate:
155
+ - cattle ranchers
156
+ inconsiderate:
157
+ cattlewomen: female
158
+ cattlemen: male
159
+ - type: or
160
+ considerate:
161
+ - chairs
162
+ - chairpersons
163
+ - coordinators
164
+ inconsiderate:
165
+ chairwomen: female
166
+ chairmen: male
167
+ - type: or
168
+ considerate:
169
+ - mail carrier
170
+ - letter carrier
171
+ - postal worker
172
+ inconsiderate:
173
+ postwoman: female
174
+ mailwoman: female
175
+ postman: male
176
+ mailman: male
177
+ - type: or
178
+ considerate:
179
+ - mail carriers
180
+ - letter carriers
181
+ - postal workers
182
+ inconsiderate:
183
+ postwomen: female
184
+ mailwomen: female
185
+ postmen: male
186
+ mailmen: male
187
+ - type: or
188
+ considerate:
189
+ - officer
190
+ - police officer
191
+ inconsiderate:
192
+ policewoman: female
193
+ policeman: male
194
+ chick cop: female
195
+ - type: or
196
+ considerate:
197
+ - officers
198
+ - police officers
199
+ inconsiderate:
200
+ policewomen: female
201
+ policemen: male
202
+ - type: or
203
+ considerate:
204
+ - flight attendant
205
+ inconsiderate:
206
+ stewardess: female
207
+ steward: male
208
+ - type: or
209
+ considerate:
210
+ - flight attendants
211
+ inconsiderate:
212
+ stewardesses: female
213
+ stewards: male
214
+ - type: or
215
+ considerate:
216
+ - member of congress
217
+ - congress person
218
+ - legislator
219
+ - representative
220
+ inconsiderate:
221
+ congresswoman: female
222
+ congressman: male
223
+ - type: or
224
+ considerate:
225
+ - members of congress
226
+ - congress persons
227
+ - legislators
228
+ - representatives
229
+ inconsiderate:
230
+ congresswomen: female
231
+ congressmen: male
232
+ - type: or
233
+ considerate:
234
+ - fire fighter
235
+ - fire officer
236
+ inconsiderate:
237
+ firewoman: female
238
+ fireman: male
239
+ - type: or
240
+ considerate:
241
+ - fire fighters
242
+ inconsiderate:
243
+ firewomen: female
244
+ firemen: male
245
+ - type: or
246
+ considerate:
247
+ - fisher
248
+ - crew member
249
+ - fisherfolk
250
+ - angler
251
+ inconsiderate:
252
+ fisherwoman: female
253
+ fisherman: male
254
+ - type: or
255
+ considerate:
256
+ - fishers
257
+ inconsiderate:
258
+ fisherwomen: female
259
+ fishermen: male
260
+ - type: or
261
+ considerate:
262
+ - kinship
263
+ - community
264
+ inconsiderate:
265
+ sisterhood: female
266
+ brotherhood: male
267
+ - type: or
268
+ considerate:
269
+ - common person
270
+ - average person
271
+ inconsiderate:
272
+ common girl: female
273
+ common man: male
274
+ - type: or
275
+ considerate:
276
+ - business executive
277
+ - entrepreneur
278
+ - business person
279
+ - professional
280
+ inconsiderate:
281
+ businesswoman: female
282
+ salarywoman: female
283
+ businessman: male
284
+ salaryman: male
285
+ - type: or
286
+ considerate:
287
+ - business executives
288
+ - entrepreneurs
289
+ inconsiderate:
290
+ businesswomen: female
291
+ salarywomen: female
292
+ career girl: female
293
+ career woman: female
294
+ businessmen: male
295
+ salarymen: male
296
+ - type: or
297
+ considerate:
298
+ - cleaner
299
+ inconsiderate:
300
+ cleaning lady: female
301
+ cleaning girl: female
302
+ cleaning woman: female
303
+ janitress: female
304
+ cleaning man: male
305
+ cleaning boy: male
306
+ janitor: male
307
+ - type: or
308
+ considerate:
309
+ - cleaners
310
+ - housekeeping
311
+ inconsiderate:
312
+ cleaning ladies: female
313
+ cleaning girls: female
314
+ janitresses: female
315
+ cleaning men: male
316
+ janitors: male
317
+ - type: or
318
+ considerate:
319
+ - courier
320
+ - messenger
321
+ inconsiderate:
322
+ delivery girl: female
323
+ delivery boy: male
324
+ - type: or
325
+ considerate:
326
+ - supervisor
327
+ - shift boss
328
+ inconsiderate:
329
+ forewoman: female
330
+ foreman: male
331
+ - type: or
332
+ considerate:
333
+ - lead
334
+ - front
335
+ - figurehead
336
+ inconsiderate:
337
+ 'frontwoman, front woman': female
338
+ 'frontman, front man': male
339
+ - type: or
340
+ considerate:
341
+ - figureheads
342
+ inconsiderate:
343
+ 'front women, frontwomen': female
344
+ 'front men, frontmen': male
345
+ - type: or
346
+ considerate:
347
+ - supervisors
348
+ - shift bosses
349
+ inconsiderate:
350
+ forewomen: female
351
+ foremen: male
352
+ - type: or
353
+ considerate:
354
+ - insurance agent
355
+ inconsiderate:
356
+ insurance woman: female
357
+ insurance man: male
358
+ - type: or
359
+ considerate:
360
+ - insurance agents
361
+ inconsiderate:
362
+ insurance women: female
363
+ insurance men: male
364
+ - type: or
365
+ considerate:
366
+ - proprietor
367
+ - building manager
368
+ inconsiderate:
369
+ landlady: female
370
+ landlord: male
371
+ - type: or
372
+ considerate:
373
+ - proprietors
374
+ - building managers
375
+ inconsiderate:
376
+ landladies: female
377
+ landlords: male
378
+ - type: or
379
+ considerate:
380
+ - graduate
381
+ inconsiderate:
382
+ alumna: female
383
+ alumnus: male
384
+ - type: or
385
+ considerate:
386
+ - graduates
387
+ inconsiderate:
388
+ alumnae: female
389
+ alumni: male
390
+ - type: or
391
+ considerate:
392
+ - anchor
393
+ - journalist
394
+ inconsiderate:
395
+ newswoman: female
396
+ newspaperwoman: female
397
+ anchorwoman: female
398
+ newsman: male
399
+ newspaperman: male
400
+ anchorman: male
401
+ - type: or
402
+ considerate:
403
+ - anchors
404
+ - journalists
405
+ inconsiderate:
406
+ newswomen: female
407
+ newspaperwomen: female
408
+ anchorwomen: female
409
+ newsmen: male
410
+ newspapermen: male
411
+ anchormen: male
412
+ - type: or
413
+ considerate:
414
+ - repairer
415
+ - technician
416
+ inconsiderate:
417
+ repairwoman: female
418
+ repairman: male
419
+ - type: or
420
+ considerate:
421
+ - technicians
422
+ inconsiderate:
423
+ repairwomen: female
424
+ repairmen: male
425
+ - type: or
426
+ considerate:
427
+ - salesperson
428
+ - sales clerk
429
+ - sales rep
430
+ - sales agent
431
+ - sales attendant
432
+ - seller
433
+ - shop assistant
434
+ inconsiderate:
435
+ saleswoman: female
436
+ sales woman: female
437
+ saleslady: female
438
+ salesman: male
439
+ sales man: male
440
+ - type: or
441
+ considerate:
442
+ - sales clerks
443
+ - sales reps
444
+ - sales agents
445
+ - sellers
446
+ inconsiderate:
447
+ saleswomen: female
448
+ sales women: female
449
+ salesladies: female
450
+ salesmen: male
451
+ sales men: male
452
+ - type: or
453
+ considerate:
454
+ - soldier
455
+ - service representative
456
+ inconsiderate:
457
+ servicewoman: female
458
+ serviceman: male
459
+ - type: or
460
+ considerate:
461
+ - soldiers
462
+ - service representatives
463
+ inconsiderate:
464
+ servicewomen: female
465
+ servicemen: male
466
+ - type: or
467
+ considerate:
468
+ - server
469
+ inconsiderate:
470
+ waitress: female
471
+ waiter: male
472
+ - type: or
473
+ considerate:
474
+ - servers
475
+ inconsiderate:
476
+ waitresses: female
477
+ waiters: male
478
+ - type: or
479
+ considerate:
480
+ - worker
481
+ - wage earner
482
+ - taxpayer
483
+ inconsiderate:
484
+ workwoman: female
485
+ working woman: female
486
+ workman: male
487
+ working man: male
488
+ - type: or
489
+ considerate:
490
+ - workers
491
+ inconsiderate:
492
+ workwomen: female
493
+ workmen: male
494
+ - type: or
495
+ considerate:
496
+ - performer
497
+ - star
498
+ - artist
499
+ - entertainer
500
+ inconsiderate:
501
+ actress: female
502
+ actor: male
503
+ - type: or
504
+ considerate:
505
+ - performers
506
+ - stars
507
+ - artists
508
+ - entertainers
509
+ inconsiderate:
510
+ actresses: female
511
+ actors: male
512
+ - type: or
513
+ considerate:
514
+ - pilot
515
+ - aviator
516
+ - airstaff
517
+ inconsiderate:
518
+ aircrewwoman: female
519
+ aircrew woman: female
520
+ aircrewman: male
521
+ airman: male
522
+ - type: or
523
+ considerate:
524
+ - pilots
525
+ - aviators
526
+ - airstaff
527
+ inconsiderate:
528
+ aircrewwomen: female
529
+ aircrew women: female
530
+ aircrewmen: male
531
+ airmen: male
532
+ - type: or
533
+ considerate:
534
+ - cabinet member
535
+ inconsiderate:
536
+ alderwoman: female
537
+ alderman: male
538
+ - type: or
539
+ considerate:
540
+ - cabinet
541
+ - cabinet members
542
+ - alderperson
543
+ inconsiderate:
544
+ alderwomen: female
545
+ aldermen: male
546
+ - type: or
547
+ considerate:
548
+ - assembly person
549
+ - assembly worker
550
+ inconsiderate:
551
+ assemblywoman: female
552
+ assemblyman: male
553
+ - type: or
554
+ considerate:
555
+ - relative
556
+ inconsiderate:
557
+ kinswoman: female
558
+ aunt: female
559
+ kinsman: male
560
+ uncle: male
561
+ - type: or
562
+ considerate:
563
+ - relatives
564
+ inconsiderate:
565
+ kinswomen: female
566
+ aunts: female
567
+ kinsmen: male
568
+ uncles: male
569
+ - type: or
570
+ considerate:
571
+ - boogeymonster
572
+ inconsiderate:
573
+ boogeywoman: female
574
+ boogeyman: male
575
+ - type: or
576
+ considerate:
577
+ - boogeymonster
578
+ inconsiderate:
579
+ boogiewoman: female
580
+ boogieman: male
581
+ - type: or
582
+ considerate:
583
+ - bogeymonster
584
+ inconsiderate:
585
+ bogeywoman: female
586
+ bogeyman: male
587
+ - type: or
588
+ considerate:
589
+ - bogeymonster
590
+ inconsiderate:
591
+ bogiewoman: female
592
+ bogieman: male
593
+ - type: or
594
+ considerate:
595
+ - boogeymonsters
596
+ inconsiderate:
597
+ boogiewomen: female
598
+ boogiemen: male
599
+ - type: or
600
+ considerate:
601
+ - bogeymonsters
602
+ inconsiderate:
603
+ bogiewomen: female
604
+ bogiemen: male
605
+ - type: or
606
+ considerate:
607
+ - bonder
608
+ inconsiderate:
609
+ bondswoman: female
610
+ bondsman: male
611
+ - type: or
612
+ considerate:
613
+ - bonders
614
+ inconsiderate:
615
+ bondswomen: female
616
+ bondsmen: male
617
+ - type: or
618
+ source: https://www.bustle.com/articles/108321-6-reasons-to-refer-to-your-significant-other-as-your-partner
619
+ considerate:
620
+ - partner
621
+ - significant other
622
+ - spouse
623
+ inconsiderate:
624
+ wife: female
625
+ husband: male
626
+ - type: or
627
+ source: https://www.bustle.com/articles/108321-6-reasons-to-refer-to-your-significant-other-as-your-partner
628
+ considerate:
629
+ - partners
630
+ - significant others
631
+ - spouses
632
+ inconsiderate:
633
+ wives: female
634
+ husbands: male
635
+ - type: or
636
+ source: https://www.bustle.com/articles/108321-6-reasons-to-refer-to-your-significant-other-as-your-partner
637
+ considerate:
638
+ - partner
639
+ - friend
640
+ - significant other
641
+ inconsiderate:
642
+ girlfriend: female
643
+ boyfriend: male
644
+ - type: or
645
+ source: https://www.bustle.com/articles/108321-6-reasons-to-refer-to-your-significant-other-as-your-partner
646
+ considerate:
647
+ - partners
648
+ - friends
649
+ - significant others
650
+ inconsiderate:
651
+ girlfriends: female
652
+ boyfriends: male
653
+ - type: or
654
+ considerate:
655
+ - childhood
656
+ inconsiderate:
657
+ girlhood: female
658
+ boyhood: male
659
+ - type: or
660
+ considerate:
661
+ - childish
662
+ inconsiderate:
663
+ girly: female
664
+ girlish: female
665
+ boyish: male
666
+ - type: or
667
+ considerate:
668
+ - journeyperson
669
+ inconsiderate:
670
+ journeywoman: female
671
+ journeyman: male
672
+ - type: or
673
+ considerate:
674
+ - journeypersons
675
+ inconsiderate:
676
+ journeywomen: female
677
+ journeymen: male
678
+ - type: or
679
+ considerate:
680
+ - godparent
681
+ - elder
682
+ - patron
683
+ inconsiderate:
684
+ godmother: female
685
+ patroness: female
686
+ godfather: male
687
+ - type: or
688
+ considerate:
689
+ - grandchild
690
+ inconsiderate:
691
+ granddaughter: female
692
+ grandson: male
693
+ - type: or
694
+ considerate:
695
+ - grandchildren
696
+ inconsiderate:
697
+ granddaughters: female
698
+ grandsons: male
699
+ - type: or
700
+ considerate:
701
+ - ancestor
702
+ inconsiderate:
703
+ foremother: female
704
+ forefather: male
705
+ - type: or
706
+ considerate:
707
+ - ancestors
708
+ inconsiderate:
709
+ foremothers: female
710
+ forefathers: male
711
+ - type: or
712
+ considerate:
713
+ - grandparent
714
+ - ancestor
715
+ inconsiderate:
716
+ granny: female
717
+ grandma: female
718
+ grandmother: female
719
+ grandpappy: male
720
+ granddaddy: male
721
+ gramps: male
722
+ grandpa: male
723
+ grandfather: male
724
+ - type: or
725
+ considerate:
726
+ - grandparents
727
+ - ancestors
728
+ inconsiderate:
729
+ grandmothers: female
730
+ grandfathers: male
731
+ - type: or
732
+ considerate:
733
+ - spouse
734
+ - newlywed
735
+ inconsiderate:
736
+ bride: female
737
+ groom: male
738
+ - type: or
739
+ considerate:
740
+ - sibling
741
+ inconsiderate:
742
+ sister: female
743
+ brother: male
744
+ - type: or
745
+ considerate:
746
+ - siblings
747
+ inconsiderate:
748
+ sisters: female
749
+ brothers: male
750
+ - type: or
751
+ considerate:
752
+ - camera operator
753
+ - camera person
754
+ inconsiderate:
755
+ camerawoman: female
756
+ cameraman: male
757
+ - type: or
758
+ considerate:
759
+ - camera operators
760
+ inconsiderate:
761
+ camerawomen: female
762
+ cameramen: male
763
+ - type: or
764
+ considerate:
765
+ - troglodyte
766
+ - hominidae
767
+ inconsiderate:
768
+ cavewoman: female
769
+ caveman: male
770
+ - type: or
771
+ considerate:
772
+ - troglodytae
773
+ - troglodyti
774
+ - troglodytes
775
+ - hominids
776
+ inconsiderate:
777
+ cavewomen: female
778
+ cavemen: male
779
+ - type: or
780
+ considerate:
781
+ - clergyperson
782
+ - clergy
783
+ - cleric
784
+ inconsiderate:
785
+ clergywoman: female
786
+ clergyman: male
787
+ - type: or
788
+ considerate:
789
+ - clergies
790
+ - clerics
791
+ inconsiderate:
792
+ clergywomen: female
793
+ clergymen: male
794
+ - type: or
795
+ considerate:
796
+ - council member
797
+ inconsiderate:
798
+ councilwoman: female
799
+ councilman: male
800
+ - type: or
801
+ considerate:
802
+ - council members
803
+ inconsiderate:
804
+ councilwomen: female
805
+ councilmen: male
806
+ - type: or
807
+ considerate:
808
+ - country person
809
+ inconsiderate:
810
+ countrywoman: female
811
+ countryman: male
812
+ - type: or
813
+ considerate:
814
+ - country folk
815
+ inconsiderate:
816
+ countrywomen: female
817
+ countrymen: male
818
+ - type: or
819
+ considerate:
820
+ - artisan
821
+ - craftsperson
822
+ - skilled worker
823
+ inconsiderate:
824
+ handywoman: female
825
+ craftswoman: female
826
+ handyman: male
827
+ craftsman: male
828
+ - type: or
829
+ considerate:
830
+ - presenter
831
+ - entertainer
832
+ - emcee
833
+ inconsiderate:
834
+ hostess: female
835
+ host: male
836
+ - type: or
837
+ considerate:
838
+ - presenters
839
+ - entertainers
840
+ - emcees
841
+ inconsiderate:
842
+ hostesses: female
843
+ hosts: male
844
+ - type: or
845
+ considerate:
846
+ - artisans
847
+ - craftspersons
848
+ - skilled workers
849
+ inconsiderate:
850
+ handywomen: female
851
+ craftswomen: female
852
+ handymen: male
853
+ craftsmen: male
854
+ - type: or
855
+ considerate:
856
+ - guillotine
857
+ inconsiderate:
858
+ hangwoman: female
859
+ hangman: male
860
+ - type: or
861
+ considerate:
862
+ - guillotines
863
+ inconsiderate:
864
+ hangwomen: female
865
+ hangmen: male
866
+ - type: or
867
+ considerate:
868
+ - sidekick
869
+ inconsiderate:
870
+ henchwoman: female
871
+ henchman: male
872
+ - type: or
873
+ considerate:
874
+ - sidekicks
875
+ inconsiderate:
876
+ henchwomen: female
877
+ henchmen: male
878
+ - type: or
879
+ considerate:
880
+ - role-model
881
+ - mentor
882
+ inconsiderate:
883
+ heroine: female
884
+ hero: male
885
+ - type: or
886
+ considerate:
887
+ - role-models
888
+ - mentor
889
+ inconsiderate:
890
+ heroines: female
891
+ heroes: male
892
+ - type: or
893
+ considerate:
894
+ - parental
895
+ - warm
896
+ - intimate
897
+ inconsiderate:
898
+ maternal: female
899
+ paternal: male
900
+ fraternal: male
901
+ - type: or
902
+ considerate:
903
+ - parental
904
+ inconsiderate:
905
+ maternity: female
906
+ paternity: male
907
+ - type: or
908
+ considerate:
909
+ - parents
910
+ inconsiderate:
911
+ mamas: female
912
+ mothers: female
913
+ moms: female
914
+ mums: female
915
+ mommas: female
916
+ mommies: female
917
+ papas: male
918
+ fathers: male
919
+ dads: male
920
+ daddies: male
921
+ - type: or
922
+ considerate:
923
+ - parent
924
+ inconsiderate:
925
+ mama: female
926
+ mother: female
927
+ mom: female
928
+ mum: female
929
+ momma: female
930
+ mommy: female
931
+ papa: male
932
+ father: male
933
+ dad: male
934
+ pop: male
935
+ daddy: male
936
+ - type: or
937
+ considerate:
938
+ - child
939
+ inconsiderate:
940
+ daughter: female
941
+ son: male
942
+ - type: or
943
+ considerate:
944
+ - children
945
+ inconsiderate:
946
+ daughters: female
947
+ sons: male
948
+ - type: or
949
+ considerate:
950
+ - concierge
951
+ inconsiderate:
952
+ doorwoman: female
953
+ doorman: male
954
+ - type: or
955
+ considerate:
956
+ - concierges
957
+ inconsiderate:
958
+ doorwomen: female
959
+ doormen: male
960
+ - type: or
961
+ considerate:
962
+ - humanly
963
+ - mature
964
+ inconsiderate:
965
+ feminin: female
966
+ dudely: male
967
+ manly: male
968
+ - type: or
969
+ considerate:
970
+ - humans
971
+ inconsiderate:
972
+ females: female
973
+ males: male
974
+ - type: or
975
+ considerate:
976
+ - ruler
977
+ inconsiderate:
978
+ empress: female
979
+ queen: female
980
+ emperor: male
981
+ king: male
982
+ - type: or
983
+ considerate:
984
+ - rulers
985
+ inconsiderate:
986
+ empresses: female
987
+ queens: female
988
+ emperors: male
989
+ kings: male
990
+ - type: or
991
+ considerate:
992
+ - jumbo
993
+ - gigantic
994
+ inconsiderate:
995
+ queensize: female
996
+ kingsize: male
997
+ - type: or
998
+ considerate:
999
+ - power behind the throne
1000
+ inconsiderate:
1001
+ queenmaker: female
1002
+ kingmaker: male
1003
+ - type: or
1004
+ considerate:
1005
+ - civilian
1006
+ inconsiderate:
1007
+ laywoman: female
1008
+ layman: male
1009
+ - type: or
1010
+ considerate:
1011
+ - civilians
1012
+ inconsiderate:
1013
+ laywomen: female
1014
+ laymen: male
1015
+ - type: or
1016
+ considerate:
1017
+ - official
1018
+ - owner
1019
+ - expert
1020
+ - superior
1021
+ - chief
1022
+ - ruler
1023
+ inconsiderate:
1024
+ dame: female
1025
+ lord: male
1026
+ - type: or
1027
+ considerate:
1028
+ - officials
1029
+ - chiefs
1030
+ - rulers
1031
+ inconsiderate:
1032
+ dames: female
1033
+ lords: male
1034
+ - type: or
1035
+ considerate:
1036
+ - adulthood
1037
+ - personhood
1038
+ - maturity
1039
+ inconsiderate:
1040
+ womanhood: female
1041
+ masculinity: male
1042
+ manhood: male
1043
+ - type: or
1044
+ considerate:
1045
+ - humanity
1046
+ inconsiderate:
1047
+ femininity: female
1048
+ manliness: male
1049
+ - type: or
1050
+ considerate:
1051
+ - shooter
1052
+ inconsiderate:
1053
+ markswoman: female
1054
+ marksman: male
1055
+ - type: or
1056
+ considerate:
1057
+ - shooters
1058
+ inconsiderate:
1059
+ markswomen: female
1060
+ marksmen: male
1061
+ - type: or
1062
+ considerate:
1063
+ - intermediary
1064
+ - go-between
1065
+ inconsiderate:
1066
+ middlewoman: female
1067
+ middleman: male
1068
+ - type: or
1069
+ considerate:
1070
+ - intermediaries
1071
+ - go-betweens
1072
+ inconsiderate:
1073
+ middlewomen: female
1074
+ middlemen: male
1075
+ - type: or
1076
+ considerate:
1077
+ - milk person
1078
+ inconsiderate:
1079
+ milkwoman: female
1080
+ milkman: male
1081
+ - type: or
1082
+ considerate:
1083
+ - milk people
1084
+ inconsiderate:
1085
+ milkwomen: female
1086
+ milkmen: male
1087
+ - type: or
1088
+ considerate:
1089
+ - nibling
1090
+ - sibling’s child
1091
+ inconsiderate:
1092
+ niece: female
1093
+ nephew: male
1094
+ - type: or
1095
+ considerate:
1096
+ - niblings
1097
+ - sibling’s children
1098
+ inconsiderate:
1099
+ nieces: female
1100
+ nephews: male
1101
+ - type: or
1102
+ considerate:
1103
+ - noble
1104
+ inconsiderate:
1105
+ noblewoman: female
1106
+ nobleman: male
1107
+ - type: or
1108
+ considerate:
1109
+ - nobles
1110
+ inconsiderate:
1111
+ noblewomen: female
1112
+ noblemen: male
1113
+ - type: or
1114
+ considerate:
1115
+ - notary
1116
+ - consumer advocate
1117
+ - trouble shooter
1118
+ - omsbudperson
1119
+ - mediator
1120
+ inconsiderate:
1121
+ ombudswoman: female
1122
+ ombudsman: male
1123
+ - type: or
1124
+ considerate:
1125
+ - notaries
1126
+ - omsbudpersons
1127
+ - omsbudpeople
1128
+ - mediators
1129
+ inconsiderate:
1130
+ ombudswomen: female
1131
+ ombudsmen: male
1132
+ - type: or
1133
+ considerate:
1134
+ - heir
1135
+ inconsiderate:
1136
+ princess: female
1137
+ prince: male
1138
+ - type: or
1139
+ considerate:
1140
+ - heirs
1141
+ inconsiderate:
1142
+ princesses: female
1143
+ princes: male
1144
+ - type: or
1145
+ considerate:
1146
+ - fairy
1147
+ inconsiderate:
1148
+ sandwoman: female
1149
+ sandman: male
1150
+ - type: or
1151
+ considerate:
1152
+ - fairies
1153
+ inconsiderate:
1154
+ sandwomen: female
1155
+ sandmen: male
1156
+ - type: or
1157
+ considerate:
1158
+ - promoter
1159
+ inconsiderate:
1160
+ showwoman: female
1161
+ showman: male
1162
+ - type: or
1163
+ considerate:
1164
+ - promoters
1165
+ inconsiderate:
1166
+ showwomen: female
1167
+ show women: female
1168
+ showmen: male
1169
+ - type: or
1170
+ considerate:
1171
+ - astronaut
1172
+ inconsiderate:
1173
+ spacewoman: female
1174
+ spaceman: male
1175
+ - type: or
1176
+ considerate:
1177
+ - astronauts
1178
+ inconsiderate:
1179
+ spacewomen: female
1180
+ spacemen: male
1181
+ - type: or
1182
+ considerate:
1183
+ - speaker
1184
+ - spokesperson
1185
+ - representative
1186
+ inconsiderate:
1187
+ spokeswoman: female
1188
+ spokesman: male
1189
+ - type: or
1190
+ considerate:
1191
+ - speakers
1192
+ - spokespersons
1193
+ inconsiderate:
1194
+ spokeswomen: female
1195
+ spokesmen: male
1196
+ - type: or
1197
+ considerate:
1198
+ - athlete
1199
+ - sports person
1200
+ inconsiderate:
1201
+ sportswoman: female
1202
+ sportsman: male
1203
+ - type: or
1204
+ considerate:
1205
+ - athletes
1206
+ - sports persons
1207
+ inconsiderate:
1208
+ sportswomen: female
1209
+ sportsmen: male
1210
+ - type: or
1211
+ considerate:
1212
+ - senator
1213
+ inconsiderate:
1214
+ stateswoman: female
1215
+ statesman: male
1216
+ - type: or
1217
+ considerate:
1218
+ - step-sibling
1219
+ inconsiderate:
1220
+ stepsister: female
1221
+ stepbrother: male
1222
+ - type: or
1223
+ considerate:
1224
+ - step-siblings
1225
+ inconsiderate:
1226
+ stepsisters: female
1227
+ stepbrothers: male
1228
+ - type: or
1229
+ considerate:
1230
+ - step-parent
1231
+ inconsiderate:
1232
+ stepmom: female
1233
+ stepmother: female
1234
+ stepdad: male
1235
+ stepfather: male
1236
+ - type: or
1237
+ considerate:
1238
+ - step-parents
1239
+ inconsiderate:
1240
+ stepmothers: female
1241
+ stepfathers: male
1242
+ - type: or
1243
+ considerate:
1244
+ - titan
1245
+ inconsiderate:
1246
+ superwoman: female
1247
+ superman: male
1248
+ - type: or
1249
+ considerate:
1250
+ - titans
1251
+ inconsiderate:
1252
+ superwomen: female
1253
+ supermen: male
1254
+ - type: or
1255
+ considerate:
1256
+ - inhumane
1257
+ inconsiderate:
1258
+ unwomanly: female
1259
+ unwomenly: female
1260
+ unmanly: male
1261
+ unmenly: male
1262
+ - type: or
1263
+ considerate:
1264
+ - watcher
1265
+ inconsiderate:
1266
+ watchwoman: female
1267
+ watchman: male
1268
+ - type: or
1269
+ considerate:
1270
+ - watchers
1271
+ inconsiderate:
1272
+ watchwomen: female
1273
+ watchmen: male
1274
+ - type: or
1275
+ considerate:
1276
+ - weather forecaster
1277
+ - meteorologist
1278
+ inconsiderate:
1279
+ weatherwoman: female
1280
+ weatherman: male
1281
+ - type: or
1282
+ considerate:
1283
+ - weather forecasters
1284
+ - meteorologists
1285
+ inconsiderate:
1286
+ weatherwomen: female
1287
+ weathermen: male
1288
+ - type: or
1289
+ considerate:
1290
+ - bereaved
1291
+ inconsiderate:
1292
+ widow: female
1293
+ widows: female
1294
+ widower: male
1295
+ widowers: male
1296
+ - type: or
1297
+ considerate:
1298
+ - own person
1299
+ inconsiderate:
1300
+ own woman: female
1301
+ own man: male
1302
+ - type: basic
1303
+ considerate:
1304
+ - french
1305
+ - the french
1306
+ inconsiderate:
1307
+ frenchmen: male
1308
+ - type: basic
1309
+ considerate:
1310
+ - courteous
1311
+ - cultured
1312
+ inconsiderate:
1313
+ ladylike: female
1314
+ - type: basic
1315
+ considerate:
1316
+ - resolutely
1317
+ - bravely
1318
+ inconsiderate:
1319
+ like a man: male
1320
+ - type: basic
1321
+ considerate:
1322
+ - birth name
1323
+ inconsiderate:
1324
+ maiden name: female
1325
+ - type: basic
1326
+ considerate:
1327
+ - first voyage
1328
+ inconsiderate:
1329
+ maiden voyage: female
1330
+ - type: basic
1331
+ considerate:
1332
+ - first flight
1333
+ inconsiderate:
1334
+ maiden flight: female
1335
+ - type: basic
1336
+ considerate:
1337
+ - strong enough
1338
+ inconsiderate:
1339
+ man enough: male
1340
+ - type: basic
1341
+ considerate:
1342
+ - upstaging
1343
+ - competitiveness
1344
+ inconsiderate:
1345
+ oneupmanship: male
1346
+ - type: basic
1347
+ considerate:
1348
+ - ms.
1349
+ inconsiderate:
1350
+ miss.: female
1351
+ mrs.: female
1352
+ - type: basic
1353
+ considerate:
1354
+ - manufactured
1355
+ - artificial
1356
+ - synthetic
1357
+ - machine-made
1358
+ - constructed
1359
+ inconsiderate:
1360
+ manmade: male
1361
+ - type: basic
1362
+ considerate:
1363
+ - dynamo
1364
+ inconsiderate:
1365
+ man of action: male
1366
+ - type: basic
1367
+ considerate:
1368
+ - scholar
1369
+ - writer
1370
+ - literary figure
1371
+ inconsiderate:
1372
+ man of letters: male
1373
+ - type: basic
1374
+ considerate:
1375
+ - sophisticate
1376
+ inconsiderate:
1377
+ man of the world: male
1378
+ - type: basic
1379
+ considerate:
1380
+ - camaraderie
1381
+ - community
1382
+ - organization
1383
+ inconsiderate:
1384
+ fellowship: male
1385
+ - type: basic
1386
+ considerate:
1387
+ - first-year student
1388
+ - fresher
1389
+ inconsiderate:
1390
+ freshman: male
1391
+ freshwoman: male
1392
+ - type: basic
1393
+ considerate:
1394
+ - quality construction
1395
+ - expertise
1396
+ inconsiderate:
1397
+ workmanship: male
1398
+ - type: basic
1399
+ considerate:
1400
+ - homemaker
1401
+ - homeworker
1402
+ inconsiderate:
1403
+ housewife: female
1404
+ - type: basic
1405
+ considerate:
1406
+ - homemakers
1407
+ - homeworkers
1408
+ inconsiderate:
1409
+ housewives: female
1410
+ - type: basic
1411
+ considerate:
1412
+ - loving
1413
+ - warm
1414
+ - nurturing
1415
+ inconsiderate:
1416
+ motherly: female
1417
+ - type: basic
1418
+ considerate:
1419
+ - human resources
1420
+ - workforce
1421
+ - personnel
1422
+ - staff
1423
+ - labor
1424
+ - personnel
1425
+ - labor force
1426
+ - staffing
1427
+ - combat personnel
1428
+ inconsiderate:
1429
+ manpower: male
1430
+ - type: basic
1431
+ considerate:
1432
+ - emcee
1433
+ - moderator
1434
+ - convenor
1435
+ inconsiderate:
1436
+ master of ceremonies: male
1437
+ - type: basic
1438
+ considerate:
1439
+ - skilled
1440
+ - authoritative
1441
+ - commanding
1442
+ inconsiderate:
1443
+ masterful: male
1444
+ - type: basic
1445
+ considerate:
1446
+ - genius
1447
+ - creator
1448
+ - instigator
1449
+ - oversee
1450
+ - launch
1451
+ - originate
1452
+ inconsiderate:
1453
+ mastermind: male
1454
+ - type: basic
1455
+ considerate:
1456
+ - work of genius
1457
+ - chef d’oeuvre
1458
+ inconsiderate:
1459
+ masterpiece: male
1460
+ - type: basic
1461
+ considerate:
1462
+ - vision
1463
+ - comprehensive plan
1464
+ inconsiderate:
1465
+ masterplan: male
1466
+ - type: basic
1467
+ considerate:
1468
+ - trump card
1469
+ - stroke of genius
1470
+ inconsiderate:
1471
+ masterstroke: male
1472
+ - type: basic
1473
+ considerate:
1474
+ - fanatic
1475
+ - zealot
1476
+ - enthusiast
1477
+ inconsiderate:
1478
+ madman: male
1479
+ mad man: male
1480
+ - type: basic
1481
+ considerate:
1482
+ - fanatics
1483
+ - zealots
1484
+ - enthusiasts
1485
+ inconsiderate:
1486
+ madmen: male
1487
+ mad men: male
1488
+ - type: basic
1489
+ considerate:
1490
+ - humankind
1491
+ inconsiderate:
1492
+ mankind: male
1493
+ - type: basic
1494
+ considerate:
1495
+ - staff hour
1496
+ - hour of work
1497
+ inconsiderate:
1498
+ manhour: male
1499
+ man hour: male
1500
+ - type: basic
1501
+ considerate:
1502
+ - staff hours
1503
+ - hours of work
1504
+ - hours of labor
1505
+ - hours
1506
+ inconsiderate:
1507
+ manhours: male
1508
+ man hours: male
1509
+ - type: basic
1510
+ note: Using gender neutral language means users will help to break up gender stereotypes.
1511
+ considerate:
1512
+ - staffed
1513
+ - crewed
1514
+ - piloted
1515
+ inconsiderate:
1516
+ - manned
1517
+ - type: basic
1518
+ note: Using gender neutral language means users will help to break up gender stereotypes.
1519
+ considerate:
1520
+ - robotic
1521
+ - automated
1522
+ inconsiderate:
1523
+ - unmanned
1524
+ - type: basic
1525
+ considerate:
1526
+ - whining
1527
+ - complaining
1528
+ - crying
1529
+ inconsiderate:
1530
+ - bitching
1531
+ - moaning
1532
+ - type: basic
1533
+ considerate:
1534
+ - whine
1535
+ - complain
1536
+ - cry
1537
+ inconsiderate:
1538
+ - bitch
1539
+ - moan
1540
+ - type: basic
1541
+ considerate:
1542
+ - tank top
1543
+ - sleeveless undershirt
1544
+ inconsiderate:
1545
+ - wife beater
1546
+ - wifebeater
1547
+ - type: basic
1548
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1549
+ considerate:
1550
+ - ancient civilization
1551
+ - ancient people
1552
+ inconsiderate: ancient man
1553
+ - type: basic
1554
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1555
+ considerate:
1556
+ - author
1557
+ - writer
1558
+ inconsiderate: authoress
1559
+ - type: basic
1560
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1561
+ considerate:
1562
+ - average consumer
1563
+ - average household
1564
+ - average homemaker
1565
+ inconsiderate: average housewife
1566
+ - type: basic
1567
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1568
+ considerate: average person
1569
+ inconsiderate: average man
1570
+ - type: basic
1571
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1572
+ considerate:
1573
+ - average wage earner
1574
+ - average taxpayer
1575
+ inconsiderate: average working man
1576
+ - type: basic
1577
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1578
+ considerate: aviator
1579
+ inconsiderate: aviatrix
1580
+ - type: basic
1581
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1582
+ considerate: the human family
1583
+ inconsiderate: brotherhood of man
1584
+ - type: basic
1585
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1586
+ considerate: model
1587
+ inconsiderate: calendar girl
1588
+ - type: basic
1589
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1590
+ considerate:
1591
+ - escort
1592
+ - prostitute
1593
+ - sex worker
1594
+ inconsiderate: call girl
1595
+ - type: basic
1596
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1597
+ considerate:
1598
+ - cleric
1599
+ - practicing Christian
1600
+ - pillar of the Church
1601
+ inconsiderate: churchman
1602
+ - type: basic
1603
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1604
+ considerate:
1605
+ - english coordinator
1606
+ - senior teacher of english
1607
+ inconsiderate: english master
1608
+ - type: basic
1609
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1610
+ considerate: the english
1611
+ inconsiderate: englishmen
1612
+ - type: basic
1613
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1614
+ considerate: executor
1615
+ inconsiderate: executrix
1616
+ - type: basic
1617
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1618
+ considerate: founder of
1619
+ inconsiderate: father of *
1620
+ - type: basic
1621
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1622
+ considerate:
1623
+ - the founders
1624
+ - founding leaders
1625
+ - forebears
1626
+ inconsiderate: founding father
1627
+ - type: basic
1628
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1629
+ considerate:
1630
+ - house worker
1631
+ - domestic help
1632
+ inconsiderate: housemaid
1633
+ - type: basic
1634
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1635
+ considerate:
1636
+ - industrial civilization
1637
+ - industrial people
1638
+ inconsiderate: industrial man
1639
+ - type: basic
1640
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1641
+ considerate: doctor
1642
+ inconsiderate: lady doctor
1643
+ - type: basic
1644
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1645
+ considerate: lead
1646
+ inconsiderate: leading lady
1647
+ - type: basic
1648
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1649
+ considerate: virgin
1650
+ inconsiderate: maiden
1651
+ - type: basic
1652
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1653
+ considerate: first race
1654
+ inconsiderate: maiden race
1655
+ - type: basic
1656
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1657
+ considerate: first speech
1658
+ inconsiderate: maiden speech
1659
+ - type: basic
1660
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1661
+ considerate: staff a desk
1662
+ inconsiderate: man a desk
1663
+ - type: basic
1664
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1665
+ considerate:
1666
+ - ordinary citizen
1667
+ - typical person
1668
+ - average person
1669
+ inconsiderate: man in the street
1670
+ - type: basic
1671
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1672
+ considerate:
1673
+ - farmer
1674
+ - rural worker
1675
+ - grazier
1676
+ - landowner
1677
+ - rural community
1678
+ - country people
1679
+ - country folk
1680
+ inconsiderate: man of the land
1681
+ - type: basic
1682
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1683
+ considerate: a faithful dog
1684
+ inconsiderate: mans best friend
1685
+ - type: basic
1686
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1687
+ considerate: staff the booth
1688
+ inconsiderate: man the booth
1689
+ - type: basic
1690
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1691
+ considerate: answer the phones
1692
+ inconsiderate: man the phones
1693
+ - type: basic
1694
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1695
+ considerate:
1696
+ - a demanding task
1697
+ - a big job
1698
+ inconsiderate:
1699
+ - mansized task
1700
+ - man sized task
1701
+ - type: basic
1702
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1703
+ considerate:
1704
+ - pass key
1705
+ - original
1706
+ inconsiderate:
1707
+ - master key
1708
+ - master copy
1709
+ - type: basic
1710
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1711
+ considerate:
1712
+ - grand scheme
1713
+ - guiding principles
1714
+ inconsiderate: master plan
1715
+ - type: basic
1716
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1717
+ considerate: become skilled
1718
+ inconsiderate: master the art
1719
+ - type: basic
1720
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1721
+ considerate: scientists
1722
+ inconsiderate: men of science
1723
+ - type: basic
1724
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1725
+ considerate: birthing nurse
1726
+ inconsiderate: midwife
1727
+ - type: basic
1728
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1729
+ considerate:
1730
+ - modern civilization
1731
+ - modern people
1732
+ inconsiderate: modern man
1733
+ - type: basic
1734
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1735
+ considerate:
1736
+ - unoccupied territory
1737
+ - wasteland
1738
+ - deathtrap
1739
+ inconsiderate: no mans land
1740
+ - type: basic
1741
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1742
+ considerate: administrative staff
1743
+ inconsiderate: office girls
1744
+ - type: basic
1745
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1746
+ considerate: poet
1747
+ inconsiderate: poetess
1748
+ - type: basic
1749
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1750
+ considerate: railway worker
1751
+ inconsiderate: railwayman
1752
+ - type: basic
1753
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1754
+ considerate:
1755
+ - fair
1756
+ - sporting
1757
+ inconsiderate: sportsmanlike
1758
+ - type: basic
1759
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1760
+ considerate:
1761
+ - fairness
1762
+ - good humor
1763
+ - sense of fair play
1764
+ inconsiderate: sportsmanship
1765
+ - type: basic
1766
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1767
+ considerate: diplomatic
1768
+ inconsiderate:
1769
+ - statesmanlike
1770
+ - statesman like
1771
+ - type: basic
1772
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1773
+ considerate:
1774
+ - cattle worker
1775
+ - farmhand
1776
+ - drover
1777
+ inconsiderate: stockman
1778
+ - type: basic
1779
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1780
+ considerate: service entrance
1781
+ inconsiderate: tradesmans entrance
1782
+ - type: basic
1783
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1784
+ considerate:
1785
+ - tax commissioner
1786
+ - tax office
1787
+ - tax collector
1788
+ inconsiderate: tax man
1789
+ - type: basic
1790
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1791
+ considerate: usher
1792
+ inconsiderate: usherette
1793
+ - type: basic
1794
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1795
+ considerate: lawyer
1796
+ inconsiderate: woman lawyer
1797
+ - type: basic
1798
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1799
+ considerate: painter
1800
+ inconsiderate: woman painter
1801
+ - type: basic
1802
+ source: https://radyananda.wordpress.com/2009/06/06/nonsexist-alternative-language-handbook-for-conscious-writers/
1803
+ considerate:
1804
+ - wage or salary earning woman
1805
+ - two-income family
1806
+ inconsiderate:
1807
+ - working mother
1808
+ - working wife
app/data/en/lgbtq.yml ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - type: basic
2
+ source: https://www.glaad.org/reference/offensive
3
+ note: This term has a clinical history and is used to imply LGBTQ+ people are diseased or psychologically/emotionally disordered
4
+ considerate:
5
+ - gay
6
+ - gay man
7
+ - lesbian
8
+ - gay person/people
9
+ inconsiderate:
10
+ - homosexual
11
+ - type: basic
12
+ source: https://www.glaad.org/reference/offensive
13
+ note: Avoid labeling something as LGBTQ+ unless you would call the same thing “straight”
14
+ considerate:
15
+ - relationship
16
+ inconsiderate:
17
+ - homosexual relations
18
+ - homosexual relationship
19
+ - type: basic
20
+ source: https://www.glaad.org/reference/offensive
21
+ note: Avoid labeling something as LGBTQ+ unless you would call the same thing “straight”
22
+ considerate:
23
+ - couple
24
+ inconsiderate:
25
+ - homosexual couple
26
+ - type: basic
27
+ source: https://www.glaad.org/reference/offensive
28
+ note: Implies that being LGBTQ+ is a choice
29
+ considerate:
30
+ - sexual orientation
31
+ - orientation
32
+ inconsiderate:
33
+ - sexual preference
34
+ - type: basic
35
+ source: https://www.glaad.org/reference/offensive
36
+ note: Implies that being LGBTQ+ is a choice
37
+ considerate:
38
+ - gay lives
39
+ - gay/lesbian lives
40
+ inconsiderate:
41
+ - gay lifestyle
42
+ - homosexual lifestyle
43
+ - type: basic
44
+ source: https://www.glaad.org/reference/offensive
45
+ note: Used by anti-LGBTQ+ extremists to create a climate of fear around LGBTQ+ issues
46
+ considerate:
47
+ - gay issues
48
+ inconsiderate:
49
+ - gay agenda
50
+ - homosexual agenda
51
+ - type: basic
52
+ source: https://www.glaad.org/reference/style
53
+ note: LGBTQ+ rights are human rights
54
+ considerate:
55
+ - equal rights
56
+ - civil rights for gay people
57
+ inconsiderate:
58
+ - special rights
59
+ - gay rights
60
+ - type: basic
61
+ source: https://www.glaad.org/reference/offensive
62
+ note: Derogatory terms for LGBTQ+ people are offensive
63
+ considerate:
64
+ - gay
65
+ inconsiderate:
66
+ - fag
67
+ - faggot
68
+ - dyke
69
+ - homo
70
+ - sodomite
71
+ - type: basic
72
+ source: https://www.glaad.org/reference/style
73
+ note: Avoid using slang shorthand
74
+ considerate:
75
+ - bisexual
76
+ inconsiderate:
77
+ - bi
78
+ - type: basic
79
+ source: https://www.glaad.org/reference/style
80
+ note: Homosexual has a clinical history and is used to imply LGBTQ+ people are diseased or psychologically/emotionally disordered
81
+ considerate:
82
+ - gay marriage
83
+ - same-sex marriage
84
+ inconsiderate:
85
+ - homosexual marriage
86
+ - type: basic
87
+ source: https://www.glaad.org/reference/style
88
+ note: Derogatory terms for LGBTQ+ people are offensive
89
+ considerate:
90
+ - transgender
91
+ inconsiderate:
92
+ - tranny
93
+ - type: basic
94
+ source: https://www.glaad.org/reference/transgender
95
+ note: Avoid using outdated / offensive terms
96
+ considerate:
97
+ - cross-dresser
98
+ inconsiderate:
99
+ - transvestite
100
+ - type: basic
101
+ source: https://www.glaad.org/reference/transgender
102
+ note: Avoid overemphasizing surgery when discussing transgender people or the process of transition - it’s not a necessary component
103
+ considerate:
104
+ - transition
105
+ - gender confirmation surgery
106
+ inconsiderate:
107
+ - sexchange
108
+ - sex change
109
+ - type: basic
110
+ source: https://www.glaad.org/reference/transgender
111
+ note: Shift focus away from the assigned sex and towards the identified gender
112
+ considerate:
113
+ - sex reassignment surgery
114
+ - gender confirmation surgery
115
+ inconsiderate:
116
+ - sex change operation
117
+ - type: basic
118
+ source: https://www.glaad.org/reference/transgender
119
+ note: Transgender should be used as an adjective, not as a noun
120
+ considerate:
121
+ - transgender people
122
+ inconsiderate:
123
+ - transgenders
124
+ - type: basic
125
+ source: https://www.glaad.org/reference/transgender
126
+ note: Transgender is already an adjective
127
+ considerate:
128
+ - transgender
129
+ inconsiderate:
130
+ - transgendered
131
+ - type: basic
132
+ source: https://www.glaad.org/reference/transgender
133
+ note: This is a term used by anti-transgender activists to dehumanize transgender people and reduce who they are to a condition
134
+ considerate:
135
+ - being transgender
136
+ - the movement for transgender equality
137
+ inconsiderate:
138
+ - transgenderism
139
+ - type: basic
140
+ note: Assigned birth gender is complicated; gender identity is more than what your parents decided you were at birth
141
+ considerate:
142
+ - assigned male at birth
143
+ - designated male at birth
144
+ inconsiderate:
145
+ - biologically male
146
+ - born a man
147
+ - genetically male
148
+ - type: basic
149
+ note: Assigned birth gender is complicated; gender identity is more than what your parents decided you were at birth
150
+ considerate:
151
+ - assigned female at birth
152
+ - designated female at birth
153
+ inconsiderate:
154
+ - biologically female
155
+ - born a woman
156
+ - genetically female
157
+ - type: basic
158
+ source: https://www.glaad.org/reference/transgender
159
+ note: A term created and used by far-right extremists to oppose nondiscrimination laws that protect transgender people
160
+ considerate:
161
+ - non-discrimination law
162
+ - non-discrimination ordinance
163
+ inconsiderate:
164
+ - bathroom bill
165
+ - type: basic
166
+ source: http://www.isna.org/node/979
167
+ note: These terms are stigmatizing to patients and their families because intersex status is more complicated than the mere presence or absence of certain gonadal tissues
168
+ considerate:
169
+ - intersex
170
+ inconsiderate:
171
+ - hermaphroditic
172
+ - pseudohermaphroditic
173
+ - pseudo hermaphroditic
174
+ - type: basic
175
+ source: http://www.isna.org/node/979
176
+ note: These terms are stigmatizing to patients and their families because intersex status is more complicated than the mere presence or absence of certain gonadal tissues
177
+ considerate:
178
+ - person who is intersex
179
+ - person
180
+ - intersex person
181
+ inconsiderate:
182
+ - hermaphrodite
183
+ - pseudohermaphrodite
184
+ - pseudo hermaphrodite
185
+ - type: basic
186
+ source: https://www.reddit.com/r/asktransgender/comments/23wbq1/is_the_term_shemale_seen_as_offensive/
187
+ note: This word dehumanizes transgender people
188
+ considerate:
189
+ - transgender person
190
+ - person
191
+ inconsiderate:
192
+ - shemale
193
+ - she male
194
+ - heshe
195
+ - shehe
196
+ - type: basic
197
+ source: https://www.selfdefined.app/definitions/pronouns/
198
+ note: Preferred pronoun sounds like it is optional to use someone's correct pronoun
199
+ considerate:
200
+ - pronoun
201
+ - pronouns
202
+ inconsiderate:
203
+ - preferred pronoun
204
+ - preferred pronouns
205
+ - gender pronoun
206
+ - gender pronouns
app/data/en/misc.yml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - type: basic
2
+ note: Avoid using terms that implies colonialism/genocide against Indigenous peoples
3
+ considerate:
4
+ - keep an eye on things
5
+ - keep shop
6
+ - provide coverage
7
+ - cover things
8
+ - take charge
9
+ inconsiderate:
10
+ - man the fort
11
+ - type: basic
12
+ note: Avoid using terms that relate to gun violence.
13
+ considerate:
14
+ - go for it
15
+ - take a chance
16
+ - make a move
17
+ - take action
18
+ inconsiderate:
19
+ - pull the trigger
app/data/en/press.yml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - type: basic
2
+ source: https://www.usnews.com/news/newsgram/articles/2013/04/04/the-associated-press-revises-islamist-another-politically-charged-term
3
+ considerate:
4
+ - muslim
5
+ - person of Islamic faith
6
+ - fanatic
7
+ - zealot
8
+ - follower of islam
9
+ - follower of the islamic faith
10
+ inconsiderate:
11
+ - islamist
12
+ - type: basic
13
+ source: https://www.usnews.com/news/newsgram/articles/2013/04/04/the-associated-press-revises-islamist-another-politically-charged-term
14
+ considerate:
15
+ - muslims
16
+ - people of Islamic faith
17
+ - fanatics
18
+ - zealots
19
+ inconsiderate:
20
+ - islamists
app/data/en/race.yml ADDED
@@ -0,0 +1,370 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - type: basic
2
+ note: Avoid using the term `master`; these suggestions are for the computer term, but there are better alternatives for other cases too
3
+ considerate:
4
+ - primary
5
+ - lead
6
+ - hub
7
+ - reference
8
+ inconsiderate:
9
+ - master
10
+ - type: basic
11
+ note: Avoid using the term `master`; these suggestions are for the computer term, but there are better alternatives for other cases too
12
+ considerate:
13
+ - primaries
14
+ - hubs
15
+ - references
16
+ inconsiderate:
17
+ - masters
18
+ - type: basic
19
+ note: Avoid using the term `slave`; these suggestions are for the computer term, but there are better alternatives for other cases too
20
+ considerate:
21
+ - secondary
22
+ - worker
23
+ - replica
24
+ - node
25
+ inconsiderate:
26
+ - slave
27
+ - type: basic
28
+ note: Avoid using the term `slave`; these suggestions are for the computer term, but there are better alternatives for other cases too
29
+ considerate:
30
+ - secondaries
31
+ - workers
32
+ - replicas
33
+ - nodes
34
+ inconsiderate:
35
+ - slaves
36
+ - type: basic
37
+ considerate:
38
+ - unethical hacker
39
+ - malicious actor
40
+ inconsiderate:
41
+ - blackhat
42
+ - type: basic
43
+ considerate:
44
+ - ethical hacker
45
+ - security researcher
46
+ inconsiderate:
47
+ - whitehat
48
+ - type: basic
49
+ considerate:
50
+ - Inuit
51
+ inconsiderate:
52
+ - eskimo
53
+ - type: basic
54
+ considerate:
55
+ - Inuits
56
+ inconsiderate:
57
+ - eskimos
58
+ - type: basic
59
+ considerate:
60
+ - Asian person
61
+ inconsiderate:
62
+ - oriental
63
+ - type: basic
64
+ considerate:
65
+ - Asian people
66
+ inconsiderate:
67
+ - orientals
68
+ - type: basic
69
+ considerate:
70
+ - person of color
71
+ - people of color
72
+ inconsiderate:
73
+ - nonwhite
74
+ - non white
75
+ - type: basic
76
+ considerate:
77
+ - projects
78
+ - urban
79
+ inconsiderate:
80
+ - ghetto
81
+ - type: basic
82
+ considerate:
83
+ - Native American
84
+ inconsiderate:
85
+ - red indian
86
+ - pocahontas
87
+ - redskin
88
+ - type: basic
89
+ considerate:
90
+ - Native American People
91
+ inconsiderate:
92
+ - red indians
93
+ - redskins
94
+ - type: basic
95
+ inconsiderate:
96
+ - animal spirit
97
+ - dream catcher
98
+ - spirit animal
99
+ - totem
100
+ note: Avoid using terms that oversimplify the complex and varied beliefs of indigenous religions.
101
+ source:
102
+ - https://www.worldreligionnews.com/opinion/spirit-animal-not-joke-oppression
103
+ - https://www.spiralnature.com/spirituality/spirit-animal-cultural-appropriation
104
+ considerate:
105
+ - favorite
106
+ - inspiration
107
+ - personal interest
108
+ - personality type
109
+ - type: basic
110
+ inconsiderate:
111
+ - long time no hear
112
+ - long time no see
113
+ note: Avoid using phrases that implicitly mock people with limited knowledge of the English language.
114
+ source: https://www.npr.org/sections/codeswitch/2014/03/09/288300303/who-first-said-long-time-no-see-and-in-which-language
115
+ considerate:
116
+ - I haven’t seen you in a long time
117
+ - it’s been a long time
118
+ - type: basic
119
+ inconsiderate:
120
+ - Indian country
121
+ note: Avoid using phrases referring to the genocidal United States “Indian Removal” laws.
122
+ source:
123
+ - https://newsmaven.io/indiancountrytoday/archive/off-the-reservation-a-teachable-moment-nW1d7U0JRkOszhtg8N1V1A/
124
+ considerate:
125
+ - enemy territory
126
+ - type: basic
127
+ inconsiderate:
128
+ - jump the reservation
129
+ - off reserve
130
+ - off the reservation
131
+ note: Avoid using phrases referring to the genocidal United States “Indian Removal” laws.
132
+ source:
133
+ - http://blog.nativepartnership.org/off-the-reservation/
134
+ - https://www.wsj.com/articles/off-the-reservation-is-a-phrase-with-a-dark-past-1462552837
135
+ - https://www.npr.org/sections/codeswitch/2014/06/29/326690947/should-saying-someone-is-off-the-reservation-be-off-limits
136
+ - https://nowtoronto.com/news/native-references-and-terms-that-are-offensive-to-indigenous-people/
137
+ considerate:
138
+ - disobey
139
+ - endure
140
+ - object to
141
+ - oppose
142
+ - resist
143
+ - type: basic
144
+ inconsiderate:
145
+ - circle the wagons
146
+ - on the warpath
147
+ note: Avoid using phrases referring to colonial stereotypes regarding Native Americans.
148
+ source:
149
+ - https://idioms.thefreedictionary.com/circle+the+wagons
150
+ - https://idioms.thefreedictionary.com/go+on+the+warpath
151
+ considerate:
152
+ - defend
153
+ - type: basic
154
+ inconsiderate:
155
+ - too many chiefs
156
+ note: Avoid using phrases referring to colonial stereotypes regarding Native Americans.
157
+ source:
158
+ - https://idioms.thefreedictionary.com/too+many+chiefs+and+not+enough+Indians
159
+ considerate:
160
+ - too many chefs in the kitchen
161
+ - too many cooks spoil the broth
162
+ - type: basic
163
+ inconsiderate:
164
+ - natives are restless
165
+ - natives are becoming restless
166
+ - natives are getting restless
167
+ - natives are growing restless
168
+ note: Avoid using phrases referring to colonial stereotypes regarding indigenous peoples.
169
+ source:
170
+ - https://tvtropes.org/pmwiki/pmwiki.php/Main/TheNativesAreRestless
171
+ considerate:
172
+ - dissatisfied
173
+ - frustrated
174
+ - type: basic
175
+ inconsiderate:
176
+ - pow wow
177
+ - powwow
178
+ note: Avoid casually using this term, which refers to traditional indigenous celebration ceremonies that were banned by genocidal laws in the United States and Canada — Native people died fighting for this right.
179
+ source:
180
+ - https://twitter.com/chadloder/status/1203507070772793345
181
+ - http://nativeappropriations.com/2012/09/paul-frank-offends-every-native-person-on-the-planet-with-fashion-night-out-dream-catchin-pow-wow.html
182
+ - https://www.britannica.com/topic/powwow
183
+ - https://nowtoronto.com/news/native-references-and-terms-that-are-offensive-to-indigenous-people/
184
+ considerate:
185
+ - conference
186
+ - gathering
187
+ - meeting
188
+ - type: basic
189
+ note: Avoid using phrases referring to colonial stereotypes regarding Native Americans.
190
+ considerate:
191
+ - go back on one’s offer
192
+ inconsiderate:
193
+ - indian give
194
+ - indian giver
195
+ - type: basic
196
+ considerate:
197
+ - Filipinos
198
+ - Filipino people
199
+ inconsiderate:
200
+ - pinoys
201
+ - pinays
202
+ - type: basic
203
+ considerate:
204
+ - Arabs
205
+ - Middle Eastern People
206
+ inconsiderate:
207
+ - towel heads
208
+ - type: basic
209
+ note: Whenever possible, try to be gender inclusive.
210
+ considerate:
211
+ - Latinx
212
+ inconsiderate:
213
+ - latino
214
+ - latina
215
+ - type: basic
216
+ considerate:
217
+ - Japanese person
218
+ - Japanese people
219
+ inconsiderate:
220
+ - japs
221
+ - type: basic
222
+ considerate:
223
+ - Jewish person
224
+ inconsiderate:
225
+ - shlomo
226
+ - shyster
227
+ - hymie
228
+ - type: basic
229
+ considerate:
230
+ - a person who is not Jewish
231
+ - not Jewish
232
+ inconsiderate:
233
+ - goyim
234
+ - goyum
235
+ - goy
236
+ - type: basic
237
+ considerate:
238
+ - a Black person
239
+ inconsiderate:
240
+ - spade
241
+ - type: basic
242
+ source: en.wikipedia.org/wiki/Romani_people#cite_ref-80
243
+ note: Gypsy is insensitive, use Roma or Romani. They’re not Egyptian as the name suggests.
244
+ considerate:
245
+ - Nomad
246
+ - Traveler
247
+ - Roma
248
+ - Romani
249
+ inconsiderate:
250
+ - gyppo
251
+ - gypsy
252
+ - Gipsy
253
+ - gyp
254
+ - type: basic
255
+ note: Replace racially-charged language with more accurate and inclusive words
256
+ considerate:
257
+ - blocklist
258
+ - wronglist
259
+ - banlist
260
+ - deny list
261
+ inconsiderate:
262
+ - blacklist
263
+ - black list
264
+ - type: basic
265
+ note: Replace racially-charged language with more accurate and inclusive words
266
+ considerate:
267
+ - blocklisted
268
+ - wronglisted
269
+ - banlisted
270
+ - deny-listed
271
+ inconsiderate:
272
+ - blacklisted
273
+ - type: basic
274
+ note: Replace racially-charged language with more accurate and inclusive words
275
+ considerate:
276
+ - blocklisting
277
+ - wronglisting
278
+ - banlisting
279
+ - deny-listing
280
+ inconsiderate:
281
+ - blacklisting
282
+ - type: basic
283
+ note: Replace racially-charged language with more accurate and inclusive words
284
+ considerate:
285
+ - passlist
286
+ - alrightlist
287
+ - safelist
288
+ - allow list
289
+ inconsiderate:
290
+ - whitelist
291
+ - white list
292
+ - type: basic
293
+ note: Replace racially-charged language with more accurate and inclusive words
294
+ considerate:
295
+ - passlisted
296
+ - alrightlisted
297
+ - safelisted
298
+ - allow-listed
299
+ inconsiderate:
300
+ - whitelisted
301
+ - type: basic
302
+ note: Replace racially-charged language with more accurate and inclusive words
303
+ considerate:
304
+ - passlisting
305
+ - alrightlisting
306
+ - safelisting
307
+ - allow-listing
308
+ inconsiderate:
309
+ - whitelisting
310
+ - type: basic
311
+ note: Avoid using terms that imply a group has not changed over time and that they are inferior
312
+ considerate:
313
+ - simple
314
+ - indigenous
315
+ - hunter-gatherer
316
+ inconsiderate:
317
+ - primitive
318
+ - savage
319
+ - stone age
320
+ - type: basic
321
+ note: Avoid using terms that make some groups sound inferior
322
+ considerate:
323
+ - society
324
+ - community
325
+ inconsiderate:
326
+ - tribe
327
+ - type: basic
328
+ note: Avoid using terms that make some groups sound inferior. Replace “sophisticated” with a neutral term such as “complex”
329
+ considerate:
330
+ - complex culture
331
+ inconsiderate:
332
+ - sophisticated culture
333
+ - type: basic
334
+ note: Avoid using terms that make some groups sound inferior. Replace “sophisticated” with a neutral term such as “complex”
335
+ considerate:
336
+ - complex technology
337
+ inconsiderate:
338
+ - sophisticated technology
339
+ - type: basic
340
+ note: Avoid using `bugreport`, as the word `bugre` is a slur in Brazilian Portuguese
341
+ considerate:
342
+ - bug report
343
+ - snapshot
344
+ inconsiderate:
345
+ - bugreport
346
+ - type: basic
347
+ source: https://en.wikipedia.org/wiki/Grandfather_clause#Origin
348
+ note: Avoid using phrases referring to racist United States “Jim Crow” laws.
349
+ considerate:
350
+ - legacy policy
351
+ - legacy clause
352
+ - deprecation policy
353
+ inconsiderate:
354
+ - grandfather clause
355
+ - grandfather policy
356
+ - type: basic
357
+ source: https://en.wikipedia.org/wiki/Grandfather_clause#Origin
358
+ note: Avoid using phrases referring to racist United States “Jim Crow” laws.
359
+ considerate:
360
+ - deprecate
361
+ inconsiderate:
362
+ - grandfathering
363
+ - type: basic
364
+ source: https://en.wikipedia.org/wiki/Grandfather_clause#Origin
365
+ note: Avoid using phrases referring to racist United States “Jim Crow” laws.
366
+ considerate:
367
+ - deprecated
368
+ - legacy
369
+ inconsiderate:
370
+ - grandfathered
app/data/en/slogans.yml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ - type: basic
2
+ considerate:
3
+ - improve
4
+ inconsiderate:
5
+ - make * great again
6
+ - make * * great again
7
+ - make * * * great again
8
+ - make * * * * great again
9
+ - make * * * * * great again
app/data/en/suicide.yml ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - type: basic
2
+ source: https://www.afsp.org/news-events/for-the-media/reporting-on-suicide, https://www.speakingofsuicide.com/2013/04/13/language/
3
+ considerate:
4
+ - died by suicide
5
+ inconsiderate:
6
+ - committed suicide
7
+ - completed suicide
8
+ - type: basic
9
+ source: https://www.afsp.org/news-events/for-the-media/reporting-on-suicide, https://www.speakingofsuicide.com/2013/04/13/language/
10
+ note: Committing suicide is not successful/unsuccessful, that sends the wrong message
11
+ considerate:
12
+ - die by suicide
13
+ inconsiderate:
14
+ - commit suicide
15
+ - complete suicide
16
+ - successful suicide
17
+ - type: basic
18
+ source: https://www.afsp.org/news-events/for-the-media/reporting-on-suicide
19
+ note: Using sensational words can cause copycat suicides or contagion
20
+ considerate:
21
+ - rise in suicides
22
+ inconsiderate:
23
+ - suicide epidemic
24
+ - epidemic of suicides
25
+ - suicide pact
26
+ - type: basic
27
+ source: https://www.speakingofsuicide.com/2013/04/13/language, https://www.afsp.org/news-events/for-the-media/reporting-on-suicide
28
+ note: Attempted suicide should not be depicted as a failure
29
+ considerate:
30
+ - suicide attempt
31
+ - attempted suicide
32
+ inconsiderate:
33
+ - failed suicide
34
+ - failed attempt
35
+ - suicide failure
36
+ - type: basic
37
+ source: https://www.afsp.org/news-events/for-the-media/reporting-on-suicide
38
+ considerate:
39
+ - a note from the deceased
40
+ inconsiderate:
41
+ - suicide note
42
+ - type: basic
43
+ note: When describing the behavior of computer software, using the word “hanged” needlessly invokes the topic of death by self-harm or lynching. Consider using the word “froze” or the phrase “stopped responding to events” or “became unresponsive” instead.
44
+ considerate:
45
+ - the app froze
46
+ - the app stopped responding
47
+ - the app stopped responding to events
48
+ - the app became unresponsive
49
+ inconsiderate:
50
+ - hang
51
+ - hanged
app/main.py CHANGED
@@ -1,48 +1,5 @@
1
- from fastapi import FastAPI
2
- import logging # Import logging module
3
 
4
- # Configure basic logging
5
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
6
- logger = logging.getLogger(__name__)
7
-
8
- # Import all individual routers for different functionalities
9
- from app.routers import (
10
- grammar,
11
- punctuation,
12
- sentence_correctness,
13
- tone,
14
- voice,
15
- inclusive_language,
16
- vocabulary,
17
- conciseness,
18
- readability,
19
- paraphrase,
20
- translate,
21
- summarize
22
- )
23
-
24
- # Initialize the FastAPI application
25
- app = FastAPI()
26
-
27
- @app.get("/")
28
- def root():
29
- """
30
- Root endpoint for the API.
31
- Returns a welcome message.
32
- """
33
- logger.info("Root endpoint accessed.")
34
- return {"message": "Welcome to Grammafree API"}
35
-
36
- # Include all the individual routers for modular API structure.
37
- app.include_router(grammar.router) # Grammar correction and diffs
38
- app.include_router(punctuation.router) # Punctuation fixes
39
- app.include_router(sentence_correctness.router) # Sentence correctness feedback
40
- app.include_router(tone.router) # Tone detection and suggestions
41
- app.include_router(voice.router) # Active/Passive voice detection
42
- app.include_router(inclusive_language.router) # Inclusive language rewriting
43
- app.include_router(vocabulary.router) # Vocabulary enhancement
44
- app.include_router(conciseness.router) # Conciseness suggestions
45
- app.include_router(readability.router) # Readability scores
46
- app.include_router(paraphrase.router) # Existing paraphrasing functionality
47
- app.include_router(translate.router) # Existing translation functionality
48
- app.include_router(summarize.router) # Existing summarization functionality
 
1
+ from app.core.app import create_app
2
+ from app.core.logging import configure_logging
3
 
4
+ configure_logging()
5
+ app = create_app()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/models.py DELETED
@@ -1,108 +0,0 @@
1
- from transformers import AutoModelForSeq2SeqLM, AutoTokenizer, pipeline
2
- import torch
3
-
4
- # Set the device for model inference (CPU is used by default)
5
- # You can change to "cuda" if a compatible GPU is available for faster processing.
6
- device = torch.device("cpu")
7
-
8
- # --- Grammar model ---
9
- # Changed to deepashri/t5-small-grammar-correction, a publicly available model
10
- # for grammatical error correction. This model is fine-tuned from T5-small.
11
- grammar_tokenizer = AutoTokenizer.from_pretrained("AventIQ-AI/t5-small-grammar-correction")
12
- grammar_model = AutoModelForSeq2SeqLM.from_pretrained("AventIQ-AI/t5-small-grammar-correction").to(device)
13
-
14
- # --- FLAN-T5 for all prompts ---
15
- # Uses google/flan-t5-small for various text generation tasks based on prompts,
16
- # such as paraphrasing, summarizing, and generating tone suggestions.
17
- flan_tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-small")
18
- flan_model = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-small").to(device)
19
-
20
- # --- Translation model ---
21
- # Uses Helsinki-NLP/opus-mt-en-ROMANCE for English to Romance language translation.
22
- trans_tokenizer = AutoTokenizer.from_pretrained("Helsinki-NLP/opus-mt-en-ROMANCE")
23
- trans_model = AutoModelForSeq2SeqLM.from_pretrained("Helsinki-NLP/opus-mt-en-ROMANCE").to(device)
24
-
25
- # --- Tone classification model ---
26
- # Uses j-hartmann/emotion-english-distilroberta-base for detecting emotions/tones
27
- # within text. This provides a more nuanced analysis than simple positive/negative.
28
- # 'top_k=1' ensures that only the most confident label is returned.
29
- tone_classifier = pipeline("sentiment-analysis", model="j-hartmann/emotion-english-distilroberta-base", top_k=1)
30
-
31
- def run_grammar_correction(text: str) -> str:
32
- """
33
- Corrects the grammar of the input text using the pre-trained T5 grammar model.
34
-
35
- Args:
36
- text (str): The input text to be grammatically corrected.
37
-
38
- Returns:
39
- str: The corrected text.
40
- """
41
- # Prepare the input for the grammar model by prefixing with "grammar: " as per
42
- # the 'deepashri/t5-small-grammar-correction' model's expected input format.
43
- # Some grammar correction models expect a specific prefix like "grammar: " or "fix: ".
44
- inputs = grammar_tokenizer(f"grammar: {text}", return_tensors="pt").to(device)
45
- # Generate the corrected output
46
- outputs = grammar_model.generate(**inputs)
47
- # Decode the generated tokens back into a readable string, skipping special tokens
48
- return grammar_tokenizer.decode(outputs[0], skip_special_tokens=True)
49
-
50
- def run_flan_prompt(prompt: str) -> str:
51
- """
52
- Runs a given prompt through the FLAN-T5 model to generate a response.
53
- Includes advanced generation parameters for better output quality.
54
-
55
- Args:
56
- prompt (str): The prompt string to be processed by FLAN-T5.
57
-
58
- Returns:
59
- str: The generated text response from FLAN-T5.
60
- """
61
- # Prepare the input for the FLAN-T5 model
62
- inputs = flan_tokenizer(prompt, return_tensors="pt").to(device)
63
-
64
- # Generate the output with improved parameters:
65
- outputs = flan_model.generate(
66
- **inputs,
67
- max_new_tokens=100,
68
- num_beams=5,
69
- do_sample=True,
70
- top_k=50,
71
- top_p=0.95,
72
- temperature=0.7
73
- )
74
- # Decode the generated tokens back into a readable string
75
- return flan_tokenizer.decode(outputs[0], skip_special_tokens=True)
76
-
77
- def run_translation(text: str, target_lang: str) -> str:
78
- """
79
- Translates the input text to the target language using the Helsinki-NLP translation model.
80
-
81
- Args:
82
- text (str): The input text to be translated.
83
- target_lang (str): The target language code (e.g., "fr" for French, "es" for Spanish).
84
-
85
- Returns:
86
- str: The translated text.
87
- """
88
- # Prepare the input for the translation model by specifying the target language
89
- inputs = trans_tokenizer(f">>{target_lang}<< {text}", return_tensors="pt").to(device)
90
- # Generate the translated output
91
- outputs = trans_model.generate(**inputs)
92
- # Decode the generated tokens back into a readable string
93
- return trans_tokenizer.decode(outputs[0], skip_special_tokens=True)
94
-
95
- def classify_tone(text: str) -> str:
96
- """
97
- Classifies the emotional tone of the input text using the pre-trained emotion classifier.
98
-
99
- Args:
100
- text (str): The input text for tone classification.
101
-
102
- Returns:
103
- str: The detected emotional label (e.g., 'neutral', 'joy', 'sadness', 'anger', 'fear', 'disgust', 'surprise').
104
- """
105
- # The tone_classifier returns a list of dictionaries, where each dictionary
106
- # contains 'label' and 'score'. We extract the 'label' from the first (and only) result.
107
- result = tone_classifier(text)[0][0]
108
- return result['label']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/prompts.py DELETED
@@ -1,121 +0,0 @@
1
- def tone_prompt(text: str, tone: str) -> str:
2
- """
3
- Generates a prompt to rewrite text in a specified tone.
4
-
5
- Args:
6
- text (str): The original text.
7
- tone (str): The desired tone (e.g., "formal", "informal", "confident").
8
-
9
- Returns:
10
- str: The generated prompt.
11
- """
12
- return f"Rewrite the following text in a {tone} tone: {text}"
13
-
14
- def clarity_prompt(text: str) -> str:
15
- """
16
- Generates a prompt to make text clearer.
17
-
18
- Args:
19
- text (str): The original text.
20
-
21
- Returns:
22
- str: The generated prompt.
23
- """
24
- return f"Make this clearer: {text}"
25
-
26
- def fluency_prompt(text: str) -> str:
27
- """
28
- Generates a prompt to improve the fluency of a sentence.
29
-
30
- Args:
31
- text (str): The original sentence.
32
-
33
- Returns:
34
- str: The generated prompt.
35
- """
36
- return f"Improve the fluency of this sentence: {text}"
37
-
38
- def paraphrase_prompt(text: str) -> str:
39
- """
40
- Generates a prompt to paraphrase text.
41
-
42
- Args:
43
- text (str): The original text.
44
-
45
- Returns:
46
- str: The generated prompt.
47
- """
48
- return f"Paraphrase: {text}"
49
-
50
- def summarize_prompt(text: str) -> str:
51
- """
52
- Generates a prompt to summarize text.
53
-
54
- Args:
55
- text (str): The original text.
56
-
57
- Returns:
58
- str: The generated prompt.
59
- """
60
- return f"Summarize: {text}"
61
-
62
- def pronoun_friendly_prompt(text: str) -> str:
63
- """
64
- Generates a prompt to rewrite text using inclusive, respectful language,
65
- avoiding gender-specific pronouns.
66
-
67
- Args:
68
- text (str): The original text.
69
-
70
- Returns:
71
- str: The generated prompt.
72
- """
73
- return f"Rewrite the following text using inclusive, respectful language avoiding gender-specific pronouns: {text}"
74
-
75
- def active_voice_prompt(text: str) -> str:
76
- """
77
- Generates a prompt to detect passive/active voice and suggest an active voice version if passive.
78
-
79
- Args:
80
- text (str): The original text.
81
-
82
- Returns:
83
- str: The generated prompt.
84
- """
85
- return f"Detect if this is passive or active voice. If passive, suggest an active voice version: {text}"
86
-
87
- def tone_analysis_prompt(text: str) -> str:
88
- """
89
- Generates a prompt to analyze the tone of text and suggest improvements.
90
-
91
- Args:
92
- text (str): The original text.
93
-
94
- Returns:
95
- str: The generated prompt.
96
- """
97
- return f"Analyze the tone of the following text and suggest improvements if needed: {text}"
98
-
99
- def vocabulary_prompt(text: str) -> str:
100
- """
101
- Generates a prompt to suggest vocabulary improvements for the given text.
102
-
103
- Args:
104
- text (str): The original text.
105
-
106
- Returns:
107
- str: The generated prompt.
108
- """
109
- return f"For the following text, identify any weak or overused words and suggest stronger, more precise synonyms or alternative phrasing. Provide suggestions as a list of 'original word/phrase -> suggested word/phrase': {text}"
110
-
111
- def conciseness_prompt(text: str) -> str:
112
- """
113
- Generates a prompt to make the given text more concise.
114
-
115
- Args:
116
- text (str): The original text.
117
-
118
- Returns:
119
- str: The generated prompt.
120
- """
121
- return f"Rewrite the following text to be more concise, removing any unnecessary words or phrases while retaining the original meaning: {text}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/routers/conciseness.py DELETED
@@ -1,42 +0,0 @@
1
- from fastapi import APIRouter, Depends, HTTPException, status # Import HTTPException and status
2
- from pydantic import BaseModel
3
- from app import models, prompts
4
- from app.core.security import verify_api_key
5
- import logging # Import logging
6
-
7
- logger = logging.getLogger(__name__)
8
-
9
- router = APIRouter()
10
-
11
- class ConcisenessInput(BaseModel):
12
- """
13
- Pydantic BaseModel for validating the input request body for the /conciseness_check endpoint.
14
- It expects a single field: 'text' (string).
15
- """
16
- text: str
17
-
18
- @router.post("/conciseness_check", dependencies=[Depends(verify_api_key)])
19
- def conciseness_check(payload: ConcisenessInput):
20
- """
21
- Provides suggestions for making text more concise.
22
-
23
- Args:
24
- payload (ConcisenessInput): The request body containing the text to be analyzed.
25
-
26
- Returns:
27
- dict: A dictionary containing the concise version of the text.
28
- """
29
- text = payload.text
30
-
31
- try:
32
- concise_text = models.run_flan_prompt(prompts.conciseness_prompt(text))
33
-
34
- return {
35
- "concise_version": concise_text
36
- }
37
- except Exception as e:
38
- logger.error(f"Error in conciseness_check: {e}", exc_info=True)
39
- raise HTTPException(
40
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
41
- detail=f"An error occurred during conciseness checking: {e}"
42
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/routers/grammar.py CHANGED
@@ -1,61 +1,60 @@
1
- from fastapi import APIRouter, Depends, HTTPException, status # Import HTTPException and status
2
- from pydantic import BaseModel
3
- from app import models
4
  from app.core.security import verify_api_key
5
  import difflib
6
- import logging # Import logging
7
 
 
 
8
  logger = logging.getLogger(__name__)
9
 
10
- router = APIRouter()
11
-
12
- class GrammarInput(BaseModel):
13
- """
14
- Pydantic BaseModel for validating the input request body for the /grammar_check endpoint.
15
- It expects a single field: 'text' (string).
16
- """
17
- text: str
18
-
19
- @router.post("/grammar_check", dependencies=[Depends(verify_api_key)])
20
- def grammar_check(payload: GrammarInput):
21
- """
22
- Corrects the grammar of the provided text and shows changes.
23
-
24
- Args:
25
- payload (GrammarInput): The request body containing the text to be analyzed.
26
-
27
- Returns:
28
- dict: A dictionary containing the corrected text and a list of changes.
29
- """
30
- original_text = payload.text
31
-
32
- try:
33
- corrected_text = models.run_grammar_correction(original_text)
34
-
35
- grammar_changes = []
36
- s = difflib.SequenceMatcher(None, original_text.split(), corrected_text.split())
37
-
38
- for opcode, i1, i2, j1, j2 in s.get_opcodes():
39
- if opcode == 'replace':
40
- original_part = ' '.join(original_text.split()[i1:i2])
41
- corrected_part = ' '.join(corrected_text.split()[j1:j2])
42
- grammar_changes.append(f"'{original_part}' \u2192 '{corrected_part}'")
43
- elif opcode == 'delete':
44
- deleted_part = ' '.join(original_text.split()[i1:i2])
45
- grammar_changes.append(f"'{deleted_part}' removed")
46
- elif opcode == 'insert':
47
- inserted_part = ' '.join(corrected_text.split()[j1:j2])
48
- grammar_changes.append(f"'{inserted_part}' added")
49
-
50
- return {
51
- "grammar": {
52
- "corrected": corrected_text,
53
- "changes": grammar_changes
54
- }
55
  }
56
- except Exception as e:
57
- logger.error(f"Error in grammar_check: {e}", exc_info=True)
 
 
 
 
 
 
 
58
  raise HTTPException(
59
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
60
- detail=f"An error occurred during grammar checking: {e}"
61
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Depends, HTTPException, status
2
+ from app.schemas.base import TextOnlyRequest
3
+ from app.services.grammar import GrammarCorrector
4
  from app.core.security import verify_api_key
5
  import difflib
6
+ import logging
7
 
8
+ router = APIRouter(prefix="/grammar", tags=["Grammar"])
9
+ corrector = GrammarCorrector()
10
  logger = logging.getLogger(__name__)
11
 
12
+ def get_diff_issues(original: str, corrected: str):
13
+ matcher = difflib.SequenceMatcher(None, original, corrected)
14
+ issues = []
15
+
16
+ for tag, i1, i2, j1, j2 in matcher.get_opcodes():
17
+ if tag == 'equal':
18
+ continue
19
+
20
+ issue = {
21
+ "offset": i1,
22
+ "length": i2 - i1,
23
+ "original": original[i1:i2],
24
+ "suggestion": corrected[j1:j2],
25
+ "context_before": original[max(0, i1 - 15):i1],
26
+ "context_after": original[i2:i2 + 15],
27
+ "message": "Grammar correction",
28
+ "line": original[:i1].count("\n") + 1,
29
+ "column": i1 - original[:i1].rfind("\n") if "\n" in original[:i1] else i1 + 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  }
31
+ issues.append(issue)
32
+
33
+ return issues
34
+
35
+ @router.post("/", dependencies=[Depends(verify_api_key)])
36
+ def correct_grammar(payload: TextOnlyRequest):
37
+ text = payload.text.strip()
38
+
39
+ if not text:
40
  raise HTTPException(
41
+ status_code=status.HTTP_400_BAD_REQUEST,
42
+ detail="Input text cannot be empty."
43
  )
44
+
45
+ corrected = corrector.correct(text)
46
+
47
+ if corrected.startswith("Input text is empty."):
48
+ raise HTTPException(status_code=400, detail="Input text cannot be empty.")
49
+ elif corrected.startswith("An error occurred during grammar correction."):
50
+ raise HTTPException(status_code=500, detail=corrected)
51
+
52
+ issues = get_diff_issues(text, corrected)
53
+
54
+ return {
55
+ "grammar": {
56
+ "original_text": text,
57
+ "corrected_text_suggestion": corrected,
58
+ "issues": issues
59
+ }
60
+ }
app/routers/inclusive_language.py CHANGED
@@ -1,42 +1,11 @@
1
- from fastapi import APIRouter, Depends, HTTPException, status # Import HTTPException and status
2
- from pydantic import BaseModel
3
- from app import models, prompts
4
  from app.core.security import verify_api_key
5
- import logging # Import logging
6
 
7
- logger = logging.getLogger(__name__)
 
8
 
9
- router = APIRouter()
10
-
11
- class InclusiveLanguageInput(BaseModel):
12
- """
13
- Pydantic BaseModel for validating the input request body for the /inclusive_language endpoint.
14
- It expects a single field: 'text' (string).
15
- """
16
- text: str
17
-
18
- @router.post("/inclusive_language", dependencies=[Depends(verify_api_key)])
19
- def inclusive_language_check(payload: InclusiveLanguageInput):
20
- """
21
- Provides suggestions for rewriting text using inclusive language.
22
-
23
- Args:
24
- payload (InclusiveLanguageInput): The request body containing the text to be analyzed.
25
-
26
- Returns:
27
- dict: A dictionary containing the rewritten text with inclusive language.
28
- """
29
- text = payload.text
30
-
31
- try:
32
- inclusive_text = models.run_flan_prompt(prompts.pronoun_friendly_prompt(text))
33
-
34
- return {
35
- "inclusive_pronouns": inclusive_text
36
- }
37
- except Exception as e:
38
- logger.error(f"Error in inclusive_language_check: {e}", exc_info=True)
39
- raise HTTPException(
40
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
41
- detail=f"An error occurred during inclusive language check: {e}"
42
- )
 
1
+ from fastapi import APIRouter, Depends
2
+ from app.schemas.base import TextOnlyRequest
3
+ from app.services.inclusive_language import InclusiveLanguageChecker
4
  from app.core.security import verify_api_key
 
5
 
6
+ router = APIRouter(prefix="/inclusive-language", tags=["Inclusive Language"])
7
+ checker = InclusiveLanguageChecker()
8
 
9
+ @router.post("/", dependencies=[Depends(verify_api_key)])
10
+ def check_inclusive_language(payload: TextOnlyRequest):
11
+ return {"suggestions": checker.check(payload.text)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/routers/paraphrase.py CHANGED
@@ -1,39 +1,12 @@
1
- from fastapi import APIRouter, Depends, HTTPException, status # Import HTTPException and status
2
- from pydantic import BaseModel
3
- from app import models, prompts
4
  from app.core.security import verify_api_key
5
- import logging # Import logging
6
 
7
- logger = logging.getLogger(__name__)
 
8
 
9
- router = APIRouter()
10
-
11
- class ParaphraseInput(BaseModel): # Renamed Input to ParaphraseInput for clarity
12
- """
13
- Pydantic BaseModel for validating the input request body for the /paraphrase endpoint.
14
- It expects a single field: 'text' (string).
15
- """
16
- text: str
17
-
18
- @router.post("/paraphrase", dependencies=[Depends(verify_api_key)])
19
- def paraphrase(payload: ParaphraseInput): # Renamed input to payload for consistency
20
- """
21
- Paraphrases the provided text.
22
-
23
- Args:
24
- payload (ParaphraseInput): The request body containing the text to be paraphrased.
25
-
26
- Returns:
27
- dict: A dictionary containing the paraphrased text.
28
- """
29
- text = payload.text
30
-
31
- try:
32
- paraphrased_text = models.run_flan_prompt(prompts.paraphrase_prompt(text))
33
- return {"result": paraphrased_text}
34
- except Exception as e:
35
- logger.error(f"Error in paraphrase: {e}", exc_info=True)
36
- raise HTTPException(
37
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
38
- detail=f"An error occurred during paraphrasing: {e}"
39
- )
 
1
+ from fastapi import APIRouter, Depends
2
+ from app.schemas.base import TextOnlyRequest
3
+ from app.services.paraphrase import Paraphraser
4
  from app.core.security import verify_api_key
 
5
 
6
+ router = APIRouter(prefix="/paraphrase", tags=["Paraphrase"])
7
+ paraphraser = Paraphraser()
8
 
9
+ @router.post("/", dependencies=[Depends(verify_api_key)])
10
+ def paraphrase_text(payload: TextOnlyRequest):
11
+ result = paraphraser.paraphrase(payload.text)
12
+ return {"result": result}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/routers/punctuation.py DELETED
@@ -1,67 +0,0 @@
1
- from fastapi import APIRouter, Depends, HTTPException, status # Import HTTPException and status
2
- from pydantic import BaseModel
3
- from app.core.security import verify_api_key
4
- import language_tool_python
5
- import logging # Import logging
6
-
7
- logger = logging.getLogger(__name__)
8
-
9
- router = APIRouter()
10
-
11
- # Initialize LanguageTool. This will be the same instance as used for punctuation.
12
- # Ensure Java is installed in your environment.
13
- try:
14
- tool = language_tool_python.LanguageTool('en-US')
15
- except Exception as e:
16
- logger.error(f"Failed to initialize LanguageTool: {e}", exc_info=True)
17
- # If LanguageTool cannot be initialized, raise an error or handle gracefully
18
- # For an MVP, we might let the app start but fail on requests that use it.
19
- # A more robust solution might mark the endpoint as unavailable.
20
- tool = None # Set to None if initialization fails
21
-
22
- class PunctuationInput(BaseModel):
23
- """
24
- Pydantic BaseModel for validating the input request body for the /punctuation_check endpoint.
25
- It expects a single field: 'text' (string).
26
- """
27
- text: str
28
-
29
- @router.post("/punctuation_check", dependencies=[Depends(verify_api_key)])
30
- def punctuation_check(payload: PunctuationInput):
31
- """
32
- Checks the provided text for punctuation errors.
33
-
34
- Args:
35
- payload (PunctuationInput): The request body containing the text to be analyzed.
36
-
37
- Returns:
38
- dict: A dictionary containing a list of punctuation issues.
39
- """
40
- if tool is None:
41
- raise HTTPException(
42
- status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
43
- detail="Punctuation check service is not available (LanguageTool failed to initialize)."
44
- )
45
-
46
- text = payload.text
47
-
48
- try:
49
- matches = tool.check(text)
50
-
51
- punctuation_issues = []
52
- for m in matches:
53
- if 'PUNCTUATION' in m.ruleId.upper():
54
- punctuation_issues.append(m.message)
55
-
56
- return {
57
- "punctuation": {
58
- "issues": punctuation_issues,
59
- "suggestions": [] # Suggestions might be handled by overall grammar correction
60
- }
61
- }
62
- except Exception as e:
63
- logger.error(f"Error in punctuation_check: {e}", exc_info=True)
64
- raise HTTPException(
65
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
66
- detail=f"An error occurred during punctuation checking: {e}"
67
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/routers/readability.py CHANGED
@@ -1,55 +1,33 @@
1
- from fastapi import APIRouter, Depends, HTTPException, status # Import HTTPException and status
2
- from pydantic import BaseModel
3
  from app.core.security import verify_api_key
4
- import textstat # Import the textstat library
5
- import logging # Import logging
 
 
6
 
 
7
  logger = logging.getLogger(__name__)
8
 
9
- router = APIRouter()
 
 
 
 
 
 
 
 
10
 
11
- class ReadabilityInput(BaseModel):
12
- """
13
- Pydantic BaseModel for validating the input request body for the /readability_score endpoint.
14
- It expects a single field: 'text' (string).
15
- """
16
- text: str
17
-
18
- @router.post("/readability_score", dependencies=[Depends(verify_api_key)])
19
- def readability_score(payload: ReadabilityInput):
20
- """
21
- Calculates various readability scores for the provided text.
22
-
23
- Args:
24
- payload (ReadabilityInput): The request body containing the text to be analyzed.
25
-
26
- Returns:
27
- dict: A dictionary containing various readability scores.
28
- """
29
- text = payload.text
30
-
31
  try:
32
- # Calculate different readability scores using textstat
33
- flesch_reading_ease = textstat.flesch_reading_ease(text)
34
- flesch_kincaid_grade = textstat.flesch_kincaid_grade(text)
35
- gunning_fog = textstat.gunning_fog(text)
36
- smog_index = textstat.smog_index(text)
37
- coleman_liau_index = textstat.coleman_liau_index(text)
38
- automated_readability_index = textstat.automated_readability_index(text)
39
-
40
- return {
41
- "readability_scores": {
42
- "flesch_reading_ease": flesch_reading_ease,
43
- "flesch_kincaid_grade": flesch_kincaid_grade,
44
- "gunning_fog_index": gunning_fog,
45
- "smog_index": smog_index,
46
- "coleman_liau_index": coleman_liau_index,
47
- "automated_readability_index": automated_readability_index
48
- }
49
- }
50
  except Exception as e:
51
- logger.error(f"Error in readability_score: {e}", exc_info=True)
52
  raise HTTPException(
53
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
54
- detail=f"An error occurred during readability score calculation: {e}"
55
- )
 
1
+ from fastapi import APIRouter, Depends, HTTPException, status
2
+ from app.schemas.base import TextOnlyRequest
3
  from app.core.security import verify_api_key
4
+ from app.utils.shared import executor
5
+ import asyncio
6
+ import logging
7
+ import textstat
8
 
9
+ router = APIRouter(prefix="/readability", tags=["Readability"])
10
  logger = logging.getLogger(__name__)
11
 
12
+ def compute_readability(text: str) -> dict:
13
+ return {
14
+ "flesch_reading_ease": textstat.flesch_reading_ease(text),
15
+ "flesch_kincaid_grade": textstat.flesch_kincaid_grade(text),
16
+ "gunning_fog_index": textstat.gunning_fog(text),
17
+ "smog_index": textstat.smog_index(text),
18
+ "coleman_liau_index": textstat.coleman_liau_index(text),
19
+ "automated_readability_index": textstat.automated_readability_index(text),
20
+ }
21
 
22
+ @router.post("/", dependencies=[Depends(verify_api_key)])
23
+ async def readability_score(payload: TextOnlyRequest):
24
+ loop = asyncio.get_event_loop()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  try:
26
+ scores = await loop.run_in_executor(executor, compute_readability, payload.text.strip())
27
+ return {"readability_scores": scores}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  except Exception as e:
29
+ logger.error(f"Readability score error: {e}", exc_info=True)
30
  raise HTTPException(
31
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
32
+ detail="An error occurred while computing readability scores."
33
+ )
app/routers/rewrite.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # routers/rewrite.py
2
+
3
+ from fastapi import APIRouter, Depends
4
+ from app.schemas.base import RewriteRequest
5
+ from app.services.gpt4_rewrite import GPT4Rewriter
6
+ from app.core.security import verify_api_key
7
+
8
+ router = APIRouter(prefix="/rewrite", tags=["Rewrite"])
9
+ rewriter = GPT4Rewriter()
10
+
11
+ @router.post("/", dependencies=[Depends(verify_api_key)])
12
+ def rewrite_with_instruction(payload: RewriteRequest):
13
+ result = rewriter.rewrite(
14
+ text=payload.text,
15
+ instruction=payload.instruction,
16
+ user_api_key=payload.user_api_key
17
+ )
18
+ return {"result": result}
app/routers/sentence_correctness.py DELETED
@@ -1,62 +0,0 @@
1
- from fastapi import APIRouter, Depends, HTTPException, status # Import HTTPException and status
2
- from pydantic import BaseModel
3
- from app.core.security import verify_api_key
4
- import language_tool_python
5
- import logging # Import logging
6
-
7
- logger = logging.getLogger(__name__)
8
-
9
- router = APIRouter()
10
-
11
- # Initialize LanguageTool. This will be the same instance as used for punctuation.
12
- # Ensure Java is installed in your environment.
13
- try:
14
- tool = language_tool_python.LanguageTool('en-US')
15
- except Exception as e:
16
- logger.error(f"Failed to initialize LanguageTool for sentence correctness: {e}", exc_info=True)
17
- tool = None
18
-
19
- class SentenceCorrectnessInput(BaseModel):
20
- """
21
- Pydantic BaseModel for validating the input request body for the /sentence_correctness endpoint.
22
- It expects a single field: 'text' (string).
23
- """
24
- text: str
25
-
26
- @router.post("/sentence_correctness", dependencies=[Depends(verify_api_key)])
27
- def sentence_correctness_check(payload: SentenceCorrectnessInput):
28
- """
29
- Provides feedback on sentence-level correctness (e.g., fragments, subject-verb agreement).
30
-
31
- Args:
32
- payload (SentenceCorrectnessInput): The request body containing the text to be analyzed.
33
-
34
- Returns:
35
- dict: A dictionary containing a list of sentence correctness feedback.
36
- """
37
- if tool is None:
38
- raise HTTPException(
39
- status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
40
- detail="Sentence correctness service is not available (LanguageTool failed to initialize)."
41
- )
42
-
43
- text = payload.text
44
-
45
- try:
46
- matches = tool.check(text)
47
-
48
- sentence_correctness_feedback = []
49
- for m in matches:
50
- # Exclude punctuation issues, as they are handled in a separate endpoint
51
- if 'PUNCTUATION' not in m.ruleId.upper():
52
- sentence_correctness_feedback.append(m.message)
53
-
54
- return {
55
- "sentence_correctness": sentence_correctness_feedback
56
- }
57
- except Exception as e:
58
- logger.error(f"Error in sentence_correctness_check: {e}", exc_info=True)
59
- raise HTTPException(
60
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
61
- detail=f"An error occurred during sentence correctness checking: {e}"
62
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/routers/summarize.py DELETED
@@ -1,39 +0,0 @@
1
- from fastapi import APIRouter, Depends, HTTPException, status # Import HTTPException and status
2
- from pydantic import BaseModel
3
- from app import models, prompts
4
- from app.core.security import verify_api_key
5
- import logging # Import logging
6
-
7
- logger = logging.getLogger(__name__)
8
-
9
- router = APIRouter()
10
-
11
- class SummarizeInput(BaseModel): # Renamed Input to SummarizeInput for clarity
12
- """
13
- Pydantic BaseModel for validating the input request body for the /summarize endpoint.
14
- It expects a single field: 'text' (string).
15
- """
16
- text: str
17
-
18
- @router.post("/summarize", dependencies=[Depends(verify_api_key)])
19
- def summarize(payload: SummarizeInput): # Renamed input to payload for consistency
20
- """
21
- Summarizes the provided text.
22
-
23
- Args:
24
- payload (SummarizeInput): The request body containing the text to be summarized.
25
-
26
- Returns:
27
- dict: A dictionary containing the summarized text.
28
- """
29
- text = payload.text
30
-
31
- try:
32
- summarized_text = models.run_flan_prompt(prompts.summarize_prompt(text))
33
- return {"result": summarized_text}
34
- except Exception as e:
35
- logger.error(f"Error in summarize: {e}", exc_info=True)
36
- raise HTTPException(
37
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
38
- detail=f"An error occurred during summarization: {e}"
39
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/routers/tone.py CHANGED
@@ -1,56 +1,11 @@
1
- from fastapi import APIRouter, Depends, HTTPException, status # Import HTTPException and status
2
- from pydantic import BaseModel
3
- from app import models, prompts
4
  from app.core.security import verify_api_key
5
- import logging # Import logging
6
 
7
- logger = logging.getLogger(__name__)
 
8
 
9
- router = APIRouter()
10
-
11
- class ToneInput(BaseModel):
12
- """
13
- Pydantic BaseModel for validating the input request body for the /tone_analysis endpoint.
14
- It expects a single field: 'text' (string).
15
- """
16
- text: str
17
-
18
- @router.post("/tone_analysis", dependencies=[Depends(verify_api_key)])
19
- def tone_analysis(payload: ToneInput):
20
- """
21
- Analyzes the tone of the provided text and suggests improvements.
22
-
23
- Args:
24
- payload (ToneInput): The request body containing the text to be analyzed.
25
-
26
- Returns:
27
- dict: A dictionary containing the detected tone and a suggestion.
28
- """
29
- text = payload.text
30
-
31
- try:
32
- detected_tone = models.classify_tone(text)
33
-
34
- tone_suggestion_text = ""
35
- # Provide a simple tone suggestion based on the detected tone.
36
- # This logic can be expanded for more sophisticated suggestions based on context or user goals.
37
- if detected_tone in ["neutral", "joy", "sadness", "anger", "fear", "disgust", "surprise"]:
38
- if detected_tone in ["neutral", "joy"]:
39
- tone_suggestion_text = models.run_flan_prompt(prompts.tone_prompt(text, "formal"))
40
- else: # For emotions like anger, sadness, fear, etc., suggest a more neutral/calm tone
41
- tone_suggestion_text = models.run_flan_prompt(prompts.tone_prompt(text, "neutral and calm"))
42
- else:
43
- tone_suggestion_text = f"The detected tone '{detected_tone}' seems appropriate for general communication."
44
-
45
- return {
46
- "tone_analysis": {
47
- "detected": detected_tone,
48
- "suggestion": tone_suggestion_text
49
- }
50
- }
51
- except Exception as e:
52
- logger.error(f"Error in tone_analysis: {e}", exc_info=True)
53
- raise HTTPException(
54
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
55
- detail=f"An error occurred during tone analysis: {e}"
56
- )
 
1
+ from fastapi import APIRouter, Depends
2
+ from app.schemas.base import TextOnlyRequest
3
+ from app.services.tone_classification import ToneClassifier
4
  from app.core.security import verify_api_key
 
5
 
6
+ router = APIRouter(prefix="/tone", tags=["Tone"])
7
+ classifier = ToneClassifier()
8
 
9
+ @router.post("/", dependencies=[Depends(verify_api_key)])
10
+ def classify_tone(payload: TextOnlyRequest):
11
+ return {"result": classifier.classify(payload.text)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/routers/translate.py CHANGED
@@ -1,41 +1,15 @@
1
- from fastapi import APIRouter, Depends, HTTPException, status # Import HTTPException and status
2
- from pydantic import BaseModel
3
- from app import models
4
  from app.core.security import verify_api_key
5
- import logging # Import logging
6
 
7
- logger = logging.getLogger(__name__)
 
8
 
9
- router = APIRouter()
10
-
11
- class TranslateInput(BaseModel):
12
- """
13
- Pydantic BaseModel for validating the input request body for the /translate endpoint.
14
- It expects two fields: 'text' (string) and 'target_lang' (string).
15
- """
16
- text: str
17
- target_lang: str
18
-
19
- @router.post("/translate", dependencies=[Depends(verify_api_key)])
20
- def translate(payload: TranslateInput): # Renamed input to payload for consistency
21
- """
22
- Translates the provided text to a target language.
23
-
24
- Args:
25
- payload (TranslateInput): The request body containing the text and target language.
26
-
27
- Returns:
28
- dict: A dictionary containing the translated text.
29
- """
30
- text = payload.text
31
- target_lang = payload.target_lang
32
-
33
- try:
34
- translated_text = models.run_translation(text, target_lang)
35
- return {"result": translated_text}
36
- except Exception as e:
37
- logger.error(f"Error in translate: {e}", exc_info=True)
38
- raise HTTPException(
39
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
40
- detail=f"An error occurred during translation: {e}"
41
- )
 
1
+ from fastapi import APIRouter, Depends
2
+ from app.schemas.base import TranslateRequest
3
+ from app.services.translation import Translator
4
  from app.core.security import verify_api_key
 
5
 
6
+ router = APIRouter(prefix="/translate", tags=["Translate"])
7
+ translator = Translator()
8
 
9
+ @router.post("/to", dependencies=[Depends(verify_api_key)])
10
+ def translate_text(payload: TranslateRequest):
11
+ result = translator.translate(
12
+ text=payload.text,
13
+ target_lang=payload.target_lang
14
+ )
15
+ return {"result": result}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/routers/vocabulary.py DELETED
@@ -1,42 +0,0 @@
1
- from fastapi import APIRouter, Depends, HTTPException, status # Import HTTPException and status
2
- from pydantic import BaseModel
3
- from app import models, prompts
4
- from app.core.security import verify_api_key
5
- import logging # Import logging
6
-
7
- logger = logging.getLogger(__name__)
8
-
9
- router = APIRouter()
10
-
11
- class VocabularyInput(BaseModel):
12
- """
13
- Pydantic BaseModel for validating the input request body for the /vocabulary_suggestions endpoint.
14
- It expects a single field: 'text' (string).
15
- """
16
- text: str
17
-
18
- @router.post("/vocabulary_suggestions", dependencies=[Depends(verify_api_key)])
19
- def vocabulary_suggestions(payload: VocabularyInput):
20
- """
21
- Provides suggestions for vocabulary improvement (e.g., stronger synonyms).
22
-
23
- Args:
24
- payload (VocabularyInput): The request body containing the text to be analyzed.
25
-
26
- Returns:
27
- dict: A dictionary containing vocabulary suggestions.
28
- """
29
- text = payload.text
30
-
31
- try:
32
- suggestions_raw = models.run_flan_prompt(prompts.vocabulary_prompt(text))
33
-
34
- return {
35
- "vocabulary_suggestions": suggestions_raw
36
- }
37
- except Exception as e:
38
- logger.error(f"Error in vocabulary_suggestions: {e}", exc_info=True)
39
- raise HTTPException(
40
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
41
- detail=f"An error occurred during vocabulary suggestion: {e}"
42
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/routers/voice.py CHANGED
@@ -1,72 +1,11 @@
1
- from fastapi import APIRouter, Depends, HTTPException, status # Import HTTPException and status
2
- from pydantic import BaseModel
3
- from app import models, prompts
4
  from app.core.security import verify_api_key
5
- import spacy
6
- import logging # Import logging
7
 
8
- logger = logging.getLogger(__name__)
 
9
 
10
- router = APIRouter()
11
-
12
- # Load the spaCy English language model.
13
- try:
14
- nlp = spacy.load("en_core_web_sm")
15
- except OSError as e:
16
- logger.error(f"SpaCy model 'en_core_web_sm' not found. Please run: python -m spacy download en_core_web_sm. Error: {e}", exc_info=True)
17
- nlp = None # Set to None if initialization fails
18
- # Re-raising here to prevent server startup if critical dependency is missing
19
- raise RuntimeError("SpaCy model 'en_core_web_sm' not loaded. Please install it.")
20
-
21
-
22
- class VoiceInput(BaseModel):
23
- """
24
- Pydantic BaseModel for validating the input request body for the /voice_analysis endpoint.
25
- It expects a single field: 'text' (string).
26
- """
27
- text: str
28
-
29
- @router.post("/voice_analysis", dependencies=[Depends(verify_api_key)])
30
- def voice_analysis(payload: VoiceInput):
31
- """
32
- Detects active/passive voice and suggests improvements.
33
-
34
- Args:
35
- payload (VoiceInput): The request body containing the text to be analyzed.
36
-
37
- Returns:
38
- dict: A dictionary containing the detected voice and a suggestion.
39
- """
40
- if nlp is None:
41
- raise HTTPException(
42
- status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
43
- detail="Voice analysis service is not available (SpaCy model failed to load)."
44
- )
45
-
46
- text = payload.text
47
-
48
- try:
49
- doc = nlp(text)
50
-
51
- voice_detected = "active"
52
- voice_suggestion = "None \u2014 active voice is fine here."
53
-
54
- for token in doc:
55
- if token.dep_ == "auxpass":
56
- voice_detected = "passive"
57
- better_voice_prompt = prompts.active_voice_prompt(text)
58
- voice_suggestion = models.run_flan_prompt(better_voice_prompt)
59
- break
60
-
61
- return {
62
- "voice": {
63
- "detected": voice_detected,
64
- "suggestion": voice_suggestion
65
- }
66
- }
67
- except Exception as e:
68
- logger.error(f"Error in voice_analysis: {e}", exc_info=True)
69
- raise HTTPException(
70
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
71
- detail=f"An error occurred during voice analysis: {e}"
72
- )
 
1
+ from fastapi import APIRouter, Depends
2
+ from app.schemas.base import TextOnlyRequest
3
+ from app.services.voice_detection import VoiceDetector
4
  from app.core.security import verify_api_key
 
 
5
 
6
+ router = APIRouter(prefix="/voice", tags=["Voice"])
7
+ detector = VoiceDetector()
8
 
9
+ @router.post("/", dependencies=[Depends(verify_api_key)])
10
+ def detect_voice(payload: TextOnlyRequest):
11
+ return {"result": detector.classify(payload.text)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/schemas/base.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field
2
+
3
+ class TextOnlyRequest(BaseModel):
4
+ text: str = Field(..., example="Your input text here")
5
+
6
+ class RewriteRequest(BaseModel):
7
+ text: str = Field(..., example="Your input text here")
8
+ instruction: str = Field(..., example="Rewrite this more concisely")
9
+ user_api_key: str = Field(..., example="sk-...")
10
+
11
+ class TranslateRequest(BaseModel):
12
+ text: str = Field(..., example="Translate this")
13
+ target_lang: str = Field(..., example="fr")
app/services/__init__.py ADDED
File without changes
app/services/base.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from threading import Lock
3
+ import logging
4
+ import time
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
9
+ logging.getLogger(__name__).info(f"Using device: {DEVICE}")
10
+
11
+ _models = {}
12
+ _models_lock = Lock()
13
+
14
+
15
+ def get_cached_model(model_name: str, load_fn):
16
+ with _models_lock:
17
+ if model_name not in _models:
18
+ logging.info(f"Loading model: {model_name}")
19
+ _models[model_name] = load_fn()
20
+ return _models[model_name]
21
+
22
+
23
+ def load_with_timer(name, fn):
24
+ # TODO: Add timing later if needed
25
+ return fn()
26
+
27
+ def timed_model_load(label: str, load_fn):
28
+ start = time.time()
29
+ model = load_fn()
30
+ logger.info(f"{label} loaded in {time.time() - start:.2f}s")
31
+ return model
32
+
33
+ # Lazy spaCy loading
34
+ _nlp = None
35
+
36
+ def get_spacy():
37
+ global _nlp
38
+ if _nlp is None:
39
+ import spacy
40
+ _nlp = spacy.load("en_core_web_sm")
41
+ return _nlp
42
+
app/services/conciseness_suggestion.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from app.services.gpt4_rewrite import GPT4Rewriter
3
+ from app.core.prompts import concise_prompt
4
+
5
+ logger = logging.getLogger(__name__)
6
+ gpt4_rewriter = GPT4Rewriter()
7
+
8
+ class ConcisenessSuggester:
9
+ def suggest(self, text: str, user_api_key: str) -> str:
10
+ text = text.strip()
11
+ if not text:
12
+ logger.warning("Conciseness suggestion requested for empty input.")
13
+ return "Input text is empty."
14
+
15
+ if not user_api_key:
16
+ logger.error("Conciseness suggestion failed: Missing user_api_key.")
17
+ return "Missing OpenAI API key."
18
+
19
+ instruction = concise_prompt(text)
20
+ concise_text = gpt4_rewriter.rewrite(text, user_api_key, instruction)
21
+
22
+ if concise_text.startswith("An OpenAI API error occurred:") or \
23
+ concise_text.startswith("An unexpected error occurred:") or \
24
+ concise_text.startswith("Missing OpenAI API key.") or \
25
+ concise_text.startswith("Input text is empty."):
26
+ logger.error(f"GPT-4 conciseness suggestion failed for text: '{text[:50]}...' - {concise_text}")
27
+ return concise_text
28
+
29
+ logger.info(f"Conciseness suggestion completed for text length: {len(text)}")
30
+ return concise_text
app/services/gpt4_rewrite.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import openai
2
+ import logging
3
+ from tenacity import retry, stop_after_attempt, wait_exponential
4
+ from app.core.config import settings
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ class GPT4Rewriter:
9
+ @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
10
+ def rewrite(self, text: str, user_api_key: str, instruction: str) -> str:
11
+ if not user_api_key:
12
+ logger.error("GPT-4 rewrite requested without an OpenAI API key.")
13
+ return "Missing OpenAI API key."
14
+
15
+ text = text.strip()
16
+ instruction = instruction.strip()
17
+
18
+ if not text:
19
+ logger.warning("GPT-4 rewrite requested for empty input.")
20
+ return "Input text is empty."
21
+ if not instruction:
22
+ logger.error("GPT-4 rewrite requested without a specific instruction.")
23
+ return "Missing rewrite instruction. Please provide a clear instruction for the rewrite."
24
+
25
+ messages = [
26
+ {"role": "system", "content": instruction},
27
+ {"role": "user", "content": text},
28
+ ]
29
+
30
+ try:
31
+ client = openai.OpenAI(api_key=user_api_key)
32
+ response = client.chat.completions.create(
33
+ model=settings.openai_model,
34
+ messages=messages,
35
+ temperature=settings.openai_temperature,
36
+ max_tokens=settings.openai_max_tokens
37
+ )
38
+ return response.choices[0].message.content.strip()
39
+ except openai.APIError as e:
40
+ logger.error(f"OpenAI API error during GPT-4 rewrite: {e}")
41
+ return f"An OpenAI API error occurred: {e}"
42
+ except Exception as e:
43
+ logger.error(f"Unexpected error during GPT-4 rewrite: {e}")
44
+ return "An unexpected error occurred during GPT-4 rewrite."
app/services/grammar.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
2
+ import torch
3
+ from .base import get_cached_model, DEVICE, load_with_timer
4
+ import logging
5
+
6
+ class GrammarCorrector:
7
+ def __init__(self):
8
+ self.tokenizer, self.model = self._load_model()
9
+
10
+ def _load_model(self):
11
+ def load_fn():
12
+ tokenizer = load_with_timer("grammar_tokenizer", lambda: AutoTokenizer.from_pretrained("visheratin/t5-efficient-mini-grammar-correction"))
13
+ model = load_with_timer("grammar_model", lambda: AutoModelForSeq2SeqLM.from_pretrained("visheratin/t5-efficient-mini-grammar-correction"))
14
+ model = model.to(DEVICE).eval()
15
+ return tokenizer, model
16
+
17
+ return get_cached_model("grammar", load_fn)
18
+
19
+ def correct(self, text: str) -> str:
20
+ text = text.strip()
21
+ if not text:
22
+ logging.warning("Grammar correction requested for empty input.")
23
+ return "Input text is empty."
24
+
25
+ try:
26
+ with torch.no_grad():
27
+ inputs = self.tokenizer([text], return_tensors="pt", truncation=True, padding=True).to(DEVICE)
28
+ outputs = self.model.generate(**inputs, max_length=256, num_beams=4, early_stopping=True)
29
+ return self.tokenizer.decode(outputs[0], skip_special_tokens=True)
30
+ except Exception as e:
31
+ logging.error(f"Error during grammar correction: {e}")
32
+ return "An error occurred during grammar correction."
app/services/inclusive_language.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import yaml
2
+ from pathlib import Path
3
+ from spacy.matcher import PhraseMatcher
4
+ from typing import List, Dict
5
+ from .base import get_spacy
6
+ from app.core.config import settings
7
+ import logging
8
+
9
+ class InclusiveLanguageChecker:
10
+ def __init__(self, rules_directory=settings.inclusive_rules_dir):
11
+ self.matcher = PhraseMatcher(get_spacy().vocab, attr="LOWER")
12
+ self.rules = self._load_inclusive_rules(rules_directory)
13
+ self._init_matcher()
14
+
15
+ def _load_inclusive_rules(self, directory: str) -> Dict[str, Dict]:
16
+ rules = {}
17
+ for path in Path(directory).glob("*.yml"):
18
+ try:
19
+ with open(path, encoding="utf-8") as f:
20
+ rule_list = yaml.safe_load(f)
21
+ if not isinstance(rule_list, list):
22
+ logging.warning(f"Skipping malformed rule file: {path}")
23
+ continue
24
+ for rule in rule_list:
25
+ note = rule.get("note", "")
26
+ source = rule.get("source", "")
27
+ considerate = rule.get("considerate", [])
28
+ inconsiderate = rule.get("inconsiderate", [])
29
+
30
+ if isinstance(considerate, str):
31
+ considerate = [considerate]
32
+ if isinstance(inconsiderate, str):
33
+ inconsiderate = [inconsiderate]
34
+
35
+ for phrase in inconsiderate:
36
+ key = phrase.lower()
37
+ rules[key] = {
38
+ "note": note,
39
+ "considerate": considerate,
40
+ "source": source,
41
+ "type": rule.get("type", "basic")
42
+ }
43
+ except Exception as e:
44
+ logging.error(f"Error loading inclusive language rule from {path}: {e}")
45
+ return rules
46
+
47
+ def _init_matcher(self):
48
+ for phrase in self.rules:
49
+ self.matcher.add(phrase, [get_spacy().make_doc(phrase)])
50
+ logging.info(f"Loaded {len(self.rules)} inclusive language rules.")
51
+
52
+ def check(self, text: str) -> List[Dict]:
53
+ text = text.strip()
54
+ if not text:
55
+ logging.warning("Inclusive language check requested for empty input.")
56
+ return []
57
+
58
+ nlp = get_spacy()
59
+ doc = nlp(text)
60
+ matches = self.matcher(doc)
61
+ results = []
62
+ matched_spans = set()
63
+
64
+ # 1. PhraseMatcher (exact) matches
65
+ for match_id, start, end in matches:
66
+ phrase_str = nlp.vocab.strings[match_id]
67
+ matched_spans.add((start, end))
68
+ rule = self.rules.get(phrase_str.lower())
69
+ if rule:
70
+ results.append({
71
+ "term": doc[start:end].text,
72
+ "type": rule["type"],
73
+ "note": rule["note"],
74
+ "suggestions": rule["considerate"],
75
+ "context": doc[start:end].sent.text,
76
+ "start": doc[start].idx,
77
+ "end": doc[end - 1].idx + len(doc[end - 1]),
78
+ "source": rule.get("source", "")
79
+ })
80
+
81
+ # 2. Lemma-based fallback (for plural/inflected forms)
82
+ for token in doc:
83
+ lemma = token.lemma_.lower()
84
+ span_key = (token.i, token.i + 1)
85
+ if lemma in self.rules and span_key not in matched_spans:
86
+ rule = self.rules[lemma]
87
+ results.append({
88
+ "term": token.text,
89
+ "type": rule["type"],
90
+ "note": rule["note"],
91
+ "suggestions": rule["considerate"],
92
+ "context": token.sent.text,
93
+ "start": token.idx,
94
+ "end": token.idx + len(token),
95
+ "source": rule.get("source", "")
96
+ })
97
+
98
+ return results
app/services/paraphrase.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
2
+ import torch
3
+ from .base import get_cached_model, DEVICE, timed_model_load
4
+ import logging
5
+
6
+ class Paraphraser:
7
+ def __init__(self):
8
+ self.tokenizer, self.model = self._load_model()
9
+
10
+ def _load_model(self):
11
+ def load_fn():
12
+ tokenizer = timed_model_load("paraphrase_tokenizer", lambda: AutoTokenizer.from_pretrained("humarin/chatgpt_paraphraser_on_T5_base"))
13
+ model = timed_model_load("paraphrase_model", lambda: AutoModelForSeq2SeqLM.from_pretrained("humarin/chatgpt_paraphraser_on_T5_base"))
14
+ model = model.to(DEVICE).eval()
15
+ return tokenizer, model
16
+ return get_cached_model("paraphrase", load_fn)
17
+
18
+ def paraphrase(self, text: str) -> str:
19
+ text = text.strip()
20
+ if not text:
21
+ logging.warning("Paraphrasing requested for empty input.")
22
+ return "Input text is empty."
23
+
24
+ prompt = f"paraphrase: {text} </s>"
25
+ try:
26
+ with torch.no_grad():
27
+ inputs = self.tokenizer([prompt], return_tensors="pt", padding=True, truncation=True).to(DEVICE)
28
+ outputs = self.model.generate(
29
+ **inputs,
30
+ max_length=256,
31
+ num_beams=5,
32
+ num_return_sequences=1,
33
+ early_stopping=True
34
+ )
35
+ return self.tokenizer.decode(outputs[0], skip_special_tokens=True, clean_up_tokenization_spaces=True)
36
+ except Exception as e:
37
+ logging.error(f"Error during paraphrasing: {e}")
38
+ return f"An error occurred during paraphrasing: {e}"
39
+
app/services/tone_classification.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import pipeline
2
+ import torch
3
+ from .base import get_cached_model, DEVICE
4
+ import logging
5
+
6
+ class ToneClassifier:
7
+ def __init__(self):
8
+ self.classifier = self._load_model()
9
+
10
+ def _load_model(self):
11
+ def load_fn():
12
+ return pipeline(
13
+ "sentiment-analysis",
14
+ model="j-hartmann/emotion-english-distilroberta-base",
15
+ top_k=1,
16
+ device=0 if torch.cuda.is_available() else -1
17
+ )
18
+ return get_cached_model("tone", load_fn)
19
+
20
+ def classify(self, text: str) -> str:
21
+ text = text.strip()
22
+ if not text:
23
+ logging.warning("Tone classification requested for empty input.")
24
+ return "Input text is empty."
25
+
26
+ try:
27
+ result = self.classifier(text)
28
+ if isinstance(result, list):
29
+ if isinstance(result[0], list):
30
+ result = result[0]
31
+ if result:
32
+ return result[0]['label']
33
+ return "Unknown"
34
+ except Exception as e:
35
+ logging.error(f"Error during tone classification: {e}")
36
+ return "An error occurred during tone classification."
app/services/translation.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
2
+ import torch
3
+ import logging
4
+ from .base import get_cached_model, DEVICE, timed_model_load
5
+ from app.core.config import settings
6
+
7
+ class Translator:
8
+ def __init__(self):
9
+ self.tokenizer, self.model = self._load_model()
10
+
11
+ def _load_model(self):
12
+ def load_fn():
13
+ tokenizer = timed_model_load("translate_tokenizer", lambda: AutoTokenizer.from_pretrained("Helsinki-NLP/opus-mt-en-ROMANCE"))
14
+ model = timed_model_load("translate_model", lambda: AutoModelForSeq2SeqLM.from_pretrained("Helsinki-NLP/opus-mt-en-ROMANCE"))
15
+ model = model.to(DEVICE).eval()
16
+ return tokenizer, model
17
+ return get_cached_model("translate", load_fn)
18
+
19
+ def translate(self, text: str, target_lang: str) -> str:
20
+ text = text.strip()
21
+ target_lang = target_lang.strip()
22
+
23
+ if not text:
24
+ logging.warning("Translation requested for empty input.")
25
+ return "Input text is empty."
26
+ if not target_lang:
27
+ logging.warning("Translation requested without a target language.")
28
+ return "Target language is empty."
29
+ if target_lang not in settings.supported_translation_languages:
30
+ return f"Unsupported target language: {target_lang}"
31
+
32
+ prompt = f">>{target_lang}<< {text}"
33
+ try:
34
+ with torch.no_grad():
35
+ inputs = self.tokenizer([prompt], return_tensors="pt", truncation=True, padding=True).to(DEVICE)
36
+ outputs = self.model.generate(**inputs, max_length=256, num_beams=1, early_stopping=True)
37
+ return self.tokenizer.decode(outputs[0], skip_special_tokens=True, clean_up_tokenization_spaces=True)
38
+ except Exception as e:
39
+ logging.error(f"Error during translation: {e}")
40
+ return f"An error occurred during translation: {e}"
app/services/vocabulary_enhancement.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from app.services.gpt4_rewrite import GPT4Rewriter
3
+ from app.core.prompts import vocabulary_prompt
4
+
5
+ logger = logging.getLogger(__name__)
6
+ gpt4_rewriter = GPT4Rewriter()
7
+
8
+ class VocabularyEnhancer:
9
+ def enhance(self, text: str, user_api_key: str) -> str:
10
+ text = text.strip()
11
+ if not text:
12
+ logger.warning("Vocabulary enhancement requested for empty input.")
13
+ return "Input text is empty."
14
+
15
+ if not user_api_key:
16
+ logger.error("Vocabulary enhancement failed: Missing user_api_key.")
17
+ return "Missing OpenAI API key."
18
+
19
+ instruction = vocabulary_prompt(text)
20
+ enhanced_text = gpt4_rewriter.rewrite(text, user_api_key, instruction)
21
+
22
+ if enhanced_text.startswith("An OpenAI API error occurred:") or \
23
+ enhanced_text.startswith("An unexpected error occurred:") or \
24
+ enhanced_text.startswith("Missing OpenAI API key.") or \
25
+ enhanced_text.startswith("Input text is empty."):
26
+ logger.error(f"GPT-4 vocabulary enhancement failed for text: '{text[:50]}...' - {enhanced_text}")
27
+ return enhanced_text
28
+
29
+ logger.info(f"Vocabulary enhancement completed for text length: {len(text)}")
30
+ return enhanced_text
app/services/voice_detection.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from .base import get_spacy
3
+
4
+ class VoiceDetector:
5
+ def __init__(self):
6
+ self.nlp = get_spacy()
7
+
8
+ def classify(self, text: str) -> str:
9
+ text = text.strip()
10
+ if not text:
11
+ logging.warning("Voice detection requested for empty input.")
12
+ return "Input text is empty."
13
+
14
+ try:
15
+ doc = self.nlp(text)
16
+ passive_sentences = 0
17
+ total_sentences = 0
18
+
19
+ for sent in doc.sents:
20
+ total_sentences += 1
21
+ for token in sent:
22
+ if token.dep_ == "nsubjpass":
23
+ passive_sentences += 1
24
+ break
25
+
26
+ if total_sentences == 0:
27
+ return "Unknown"
28
+
29
+ ratio = passive_sentences / total_sentences
30
+ return "Passive" if ratio > 0.5 else "Active"
31
+ except Exception as e:
32
+ logging.error(f"Error during voice detection: {e}")
33
+ return "An error occurred during voice detection."
app/test/test_rewrite.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+ from fastapi.testclient import TestClient
3
+ from main import create_app
4
+
5
+ client = TestClient(create_app())
6
+
7
+ def test_rewrite_success():
8
+ payload = {
9
+ "text": "This is a very long and unnecessarily wordy sentence that could be simpler.",
10
+ "instruction": "Make this more concise"
11
+ }
12
+ headers = {"x-api-key": "your-secret-key"}
13
+ response = client.post("/rewrite/", json=payload, headers=headers)
14
+ assert response.status_code == 200
15
+ assert "result" in response.json()
app/test_main.py DELETED
@@ -1,39 +0,0 @@
1
- from fastapi.testclient import TestClient
2
- from app.main import app
3
-
4
- client = TestClient(app)
5
-
6
- def test_grammar():
7
- response = client.post("/rewrite", json={"text": "She go to school yesterday.", "mode": "grammar"})
8
- assert response.status_code == 200
9
- assert "result" in response.json()
10
-
11
- def test_paraphrase():
12
- response = client.post("/rewrite", json={"text": "I love programming.", "mode": "paraphrase"})
13
- assert response.status_code == 200
14
- assert "result" in response.json()
15
-
16
- def test_clarity():
17
- response = client.post("/rewrite", json={"text": "This sentence is a bit confusing.", "mode": "clarity"})
18
- assert response.status_code == 200
19
- assert "result" in response.json()
20
-
21
- def test_fluency():
22
- response = client.post("/rewrite", json={"text": "He no went there before.", "mode": "fluency"})
23
- assert response.status_code == 200
24
- assert "result" in response.json()
25
-
26
- def test_tone():
27
- response = client.post("/rewrite", json={"text": "Leave me alone.", "mode": "tone", "tone": "friendly"})
28
- assert response.status_code == 200
29
- assert "result" in response.json()
30
-
31
- def test_translation():
32
- response = client.post("/rewrite", json={"text": "How are you?", "mode": "translate", "target_lang": "fr"})
33
- assert response.status_code == 200
34
- assert "result" in response.json()
35
-
36
- def test_pronoun():
37
- response = client.post("/rewrite", json={"text": "Each user must provide his email.", "mode": "pronoun"})
38
- assert response.status_code == 200
39
- assert "result" in response.json()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/utils/shared.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from concurrent.futures import ThreadPoolExecutor
2
+
3
+ # Shared executor for all CPU-bound tasks
4
+ executor = ThreadPoolExecutor(max_workers=4)
requirements.txt CHANGED
@@ -6,6 +6,6 @@ sentencepiece
6
  pyspellchecker
7
  spacy
8
  nltk
9
- language-tool-python
10
  scikit-learn
11
  textstat
 
 
6
  pyspellchecker
7
  spacy
8
  nltk
 
9
  scikit-learn
10
  textstat
11
+ numpy>=1.21
run.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # run.py (no change needed for path handling if app.main:app is used)
2
+ import uvicorn
3
+ import os
4
+ import sys
5
+ # from pathlib import Path # No longer strictly necessary to add project_root to sys.path explicitly here
6
+ # sys.path.insert(0, str(project_root)) # Can likely remove this line if your only problem was 'models' import
7
+
8
+ host = "0.0.0.0"
9
+ port = 7860
10
+ app_module = "app.main:app" # This refers to the 'app' package correctly
11
+
12
+ if __name__ == "__main__":
13
+ print(f"Starting Uvicorn server for {app_module} at http://{host}:{port}")
14
+ # ... rest of your print statements and uvicorn.run call
15
+ uvicorn.run(app_module, host=host, port=port, reload=True)