From e0b7fffff7e3a1a370dbdb95a0ba5780a37cfe43 Mon Sep 17 00:00:00 2001 From: Elazarcl <elazarb@copyleaks.com> Date: Wed, 4 Sep 2024 12:29:43 +0300 Subject: [PATCH 1/3] added ai detector and writing assistant functions added ai detector and writing assistant functions --- copyleaks/clients/__init__.py | 0 copyleaks/clients/ai_detection_client.py | 81 ++++++++++++++++ copyleaks/clients/writing_assistant_client.py | 92 +++++++++++++++++++ copyleaks/copyleaks.py | 5 + copyleaks/helpers/__init__.py | 0 copyleaks/helpers/copyleaks_client_helper.py | 24 +++++ .../models/submit/ai_detection_document.py | 72 +++++++++++++++ copyleaks/models/submit/score_weights.py | 52 +++++++++++ .../submit/writing_assistant_document.py | 63 +++++++++++++ 9 files changed, 389 insertions(+) create mode 100644 copyleaks/clients/__init__.py create mode 100644 copyleaks/clients/ai_detection_client.py create mode 100644 copyleaks/clients/writing_assistant_client.py create mode 100644 copyleaks/helpers/__init__.py create mode 100644 copyleaks/helpers/copyleaks_client_helper.py create mode 100644 copyleaks/models/submit/ai_detection_document.py create mode 100644 copyleaks/models/submit/score_weights.py create mode 100644 copyleaks/models/submit/writing_assistant_document.py diff --git a/copyleaks/clients/__init__.py b/copyleaks/clients/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/copyleaks/clients/ai_detection_client.py b/copyleaks/clients/ai_detection_client.py new file mode 100644 index 0000000..3d2b5d1 --- /dev/null +++ b/copyleaks/clients/ai_detection_client.py @@ -0,0 +1,81 @@ + +''' + The MIT License(MIT) + + Copyright(c) 2016 Copyleaks LTD (https://copyleaks.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +''' + +from aifc import Error +import requests +from copyleaks.consts import Consts +from copyleaks.helpers.copyleaks_client_helper import CopyleaksClientHelper + +class _AIDetectionClient: + @staticmethod + def __submit(url, auth_token, scan_id, submission): + assert url and scan_id and submission + + CopyleaksClientHelper.verify_auth_token(auth_token) + + headers = { + 'Content-Type': 'application/json', + 'User-Agent': Consts.USER_AGENT, + 'Authorization': f"Bearer {auth_token['access_token']}" + } + json = submission.toJSON() + response = requests.post(url, headers=headers, data=json) + if response.ok: + return response.json() + elif response.status_code == 503: + raise Error() + else: + raise Error(response) + + @staticmethod + def submit_natural_language(auth_token, scan_id, submission): + ''' + Use Copyleaks AI Content Detection to differentiate between human texts and AI written texts. + This endpoint will receive submitted text to be checked. At the end of the processing stage, + the result will be shown as classifications. Text classification is divided into sections. + Each section may have a different classification + + Raises: + `CommandError`: Server reject the request. See response status code, headers and content for more info. + `UnderMaintenanceError`: Copyleaks servers are unavailable for maintenance. We recommend to implement exponential backoff algorithm as described here: https://api.copyleaks.com/documentation/v3/exponential-backoff + ''' + url = f"{Consts.API_SERVER_URI}/v2/writer-detector/{scan_id}/check" + return _AIDetectionClient.__submit(url, auth_token, scan_id, submission) + + + @staticmethod + def submit_source_code(auth_token, scan_id, submission): + ''' + Use Copyleaks AI Content Detection to differentiate between human source code and AI written source code. + This endpoint will receive submitted source code to be checked. + At the end of the processing stage, the result will be shown as classifications. + Source code classification is divided into sections. Each section may have a different classification. + + Raises: + `CommandError`: Server reject the request. See response status code, headers and content for more info. + `UnderMaintenanceError`: Copyleaks servers are unavailable for maintenance. We recommend to implement exponential backoff algorithm as described here: https://api.copyleaks.com/documentation/v3/exponential-backoff + ''' + url = f"{Consts.API_SERVER_URI}/v2/writer-detector/source-code/{scan_id}/check" + return _AIDetectionClient.__submit(url, auth_token, scan_id, submission) \ No newline at end of file diff --git a/copyleaks/clients/writing_assistant_client.py b/copyleaks/clients/writing_assistant_client.py new file mode 100644 index 0000000..8ad7028 --- /dev/null +++ b/copyleaks/clients/writing_assistant_client.py @@ -0,0 +1,92 @@ + +''' + The MIT License(MIT) + + Copyright(c) 2016 Copyleaks LTD (https://copyleaks.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +''' + +import requests +from copyleaks.consts import Consts +from copyleaks.exceptions.command_error import CommandError +from copyleaks.exceptions.under_maintenance_error import UnderMaintenanceError +from copyleaks.helpers.copyleaks_client_helper import CopyleaksClientHelper + +class _WritingAssistantClient: + @staticmethod + def __submit(url, auth_token, scan_id, submission): + assert url and scan_id and submission + + CopyleaksClientHelper.verify_auth_token(auth_token) + + headers = { + 'Content-Type': 'application/json', + 'User-Agent': Consts.USER_AGENT, + 'Authorization': f"Bearer {auth_token['access_token']}" + } + json = submission.toJSON() + response = requests.post(url, headers=headers, data=json) + if response.ok: + return response.json() + elif response.status_code == 503: + raise UnderMaintenanceError() + else: + raise CommandError(response) + + @staticmethod + def submit_text(auth_token, scan_id, submission): + ''' + Use Copyleaks Writing Assistant to generate grammar, spelling and sentence corrections for a given text. + This endpoint will receive submitted text to be checked. The response will show the suggested corrections to the input text. + + Raises: + `CommandError`: Server reject the request. See response status code, headers and content for more info. + `UnderMaintenanceError`: Copyleaks servers are unavailable for maintenance. We recommend to implement exponential backoff algorithm as described here: https://api.copyleaks.com/documentation/v3/exponential-backoff + ''' + url = f"{Consts.API_SERVER_URI}/v1/writing-feedback/{scan_id}/check" + return _WritingAssistantClient.__submit(url, auth_token, scan_id, submission) + + @staticmethod + def get_correction_types(language_code): + ''' + Get a list of correction types supported within the Writing Assistant API. + Correction types apply to all supported languages. + The supplied language code for this request is used to determine the language of the texts returned. + + Raises: + `CommandError`: Server reject the request. See response status code, headers and content for more info. + `UnderMaintenanceError`: Copyleaks servers are unavailable for maintenance. We recommend to implement exponential backoff algorithm as described here: https://api.copyleaks.com/documentation/v3/exponential-backoff + + Returns: + List of supported correction types. + ''' + + url = f"{Consts.API_SERVER_URI}/v1/writing-feedback/correction-types/{language_code}" + headers = { + 'User-Agent': Consts.USER_AGENT + } + + response = requests.get(url, headers=headers) + if response.ok: + return response.json() + elif response.status_code == 503: + raise UnderMaintenanceError() + else: + raise CommandError(response.content) diff --git a/copyleaks/copyleaks.py b/copyleaks/copyleaks.py index 941236e..3dd4f3d 100644 --- a/copyleaks/copyleaks.py +++ b/copyleaks/copyleaks.py @@ -33,10 +33,15 @@ from copyleaks.exceptions.rate_limit_error import RateLimitError from copyleaks.exceptions.auth_expired_error import AuthExipredError from enum import Enum +from copyleaks.clients.ai_detection_client import _AIDetectionClient +from copyleaks.clients.writing_assistant_client import _WritingAssistantClient class Copyleaks(object): + WritingAssistantClient = _WritingAssistantClient + AiDetectionClient = _AIDetectionClient + @staticmethod def set_identity_uri(uri): ''' diff --git a/copyleaks/helpers/__init__.py b/copyleaks/helpers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/copyleaks/helpers/copyleaks_client_helper.py b/copyleaks/helpers/copyleaks_client_helper.py new file mode 100644 index 0000000..9934b2e --- /dev/null +++ b/copyleaks/helpers/copyleaks_client_helper.py @@ -0,0 +1,24 @@ +from datetime import datetime, timedelta +import dateutil.parser +import pytz +from copyleaks.exceptions.auth_expired_error import AuthExipredError + +class CopyleaksClientHelper: + @staticmethod + def verify_auth_token(auth_token): + ''' + Verify that Copyleaks authentication token is exists and not expired. + + Parameters: + auth_token: Copyleaks authentication token + + Raises: + `AuthExipredError`: authentication expired. Need to login again. + ''' + assert auth_token and auth_token['.expires'] and auth_token['access_token'] + + now = pytz.UTC.localize(datetime.utcnow() + timedelta(0, 5 * 60)) # adds 5 minutes ahead for a safety shield. + upTo = dateutil.parser.parse(auth_token['.expires']) + + if upTo <= now: + raise AuthExipredError() # expired diff --git a/copyleaks/models/submit/ai_detection_document.py b/copyleaks/models/submit/ai_detection_document.py new file mode 100644 index 0000000..09a0dbd --- /dev/null +++ b/copyleaks/models/submit/ai_detection_document.py @@ -0,0 +1,72 @@ +''' + The MIT License(MIT) + + Copyright(c) 2016 Copyleaks LTD (https://copyleaks.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +''' + +from abc import ABC +import json + +class AiDetectionDocument(ABC): + def __init__(self, text): + assert text + self.text = text + + def get_text(self): + return self.text + + def set_text(self, value): + assert value + self.text = value + + def get_sandbox(self): + return self.sandbox + + def set_sandbox(self, value): + self.sandbox = value + + def toJSON(self): + return json.dumps(self, default=lambda o: o.__dict__, + sort_keys=True, indent=4) + + +class NaturalLanguageDocument(AiDetectionDocument): + def __init__(self, text): + super().__init__(text) + + def get_language(self): + return self.language + + def set_language(self, value): + self.language = value + + +class SourceCodeDocument(AiDetectionDocument): + def __init__(self, text, filename): + super().__init__(text) + self.filename = filename + + def get_filename(self): + return self.filename + + def set_filename(self, value): + assert value + self.filename = value diff --git a/copyleaks/models/submit/score_weights.py b/copyleaks/models/submit/score_weights.py new file mode 100644 index 0000000..fe79d86 --- /dev/null +++ b/copyleaks/models/submit/score_weights.py @@ -0,0 +1,52 @@ +''' + The MIT License(MIT) + + Copyright(c) 2016 Copyleaks LTD (https://copyleaks.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +''' + +class ScoreWeights: + def get_grammar_score_weight(self): + return self.grammarScoreWeight + + def set_grammar_score_weight(self, value): + assert value + self.grammarScoreWeight = value + + def get_mechanics_score_weight(self): + return self.mechanicsScoreWeight + + def set_mechanics_score_weight(self, value): + assert value + self.mechanicsScoreWeight = value + + def get_sentence_structure_score_weight(self): + return self.sentenceStructureScoreWeight + + def set_sentence_structure_score_weight(self, value): + assert value + self.sentenceStructureScoreWeight = value + + def get_word_choice_score_weight(self): + return self.wordChoiceScoreWeight + + def set_word_choice_score_weight(self, value): + self.wordChoiceScoreWeight = value + assert value diff --git a/copyleaks/models/submit/writing_assistant_document.py b/copyleaks/models/submit/writing_assistant_document.py new file mode 100644 index 0000000..8e3c361 --- /dev/null +++ b/copyleaks/models/submit/writing_assistant_document.py @@ -0,0 +1,63 @@ +''' + The MIT License(MIT) + + Copyright(c) 2016 Copyleaks LTD (https://copyleaks.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +''' + +import json + +class WritingAssistantDocument(): + + def __init__(self, text): + assert text + self.text = text + + def get_text(self): + return self.text + + def set_text(self, value): + assert value + self.text = value + + def get_sandbox(self): + return self.sandbox + + def set_sandbox(self, value): + assert value + self.sandbox = value + + def get_language(self): + return self.language + + def set_language(self, value): + assert value + self.language = value + + def get_score(self): + return self.score + + def set_score(self, value): + assert value + self.score = value + + def toJSON(self): + return json.dumps(self, default=lambda o: o.__dict__, + sort_keys=True, indent=4) From 162e2b1033492ef213a6ee5dd74b5731f8688095 Mon Sep 17 00:00:00 2001 From: Elazarcl <elazarb@copyleaks.com> Date: Thu, 5 Sep 2024 14:55:05 +0300 Subject: [PATCH 2/3] spacing fix spacing fix --- copyleaks/clients/ai_detection_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/copyleaks/clients/ai_detection_client.py b/copyleaks/clients/ai_detection_client.py index 3d2b5d1..0fbc679 100644 --- a/copyleaks/clients/ai_detection_client.py +++ b/copyleaks/clients/ai_detection_client.py @@ -64,7 +64,7 @@ def submit_natural_language(auth_token, scan_id, submission): url = f"{Consts.API_SERVER_URI}/v2/writer-detector/{scan_id}/check" return _AIDetectionClient.__submit(url, auth_token, scan_id, submission) - + @staticmethod def submit_source_code(auth_token, scan_id, submission): ''' @@ -78,4 +78,4 @@ def submit_source_code(auth_token, scan_id, submission): `UnderMaintenanceError`: Copyleaks servers are unavailable for maintenance. We recommend to implement exponential backoff algorithm as described here: https://api.copyleaks.com/documentation/v3/exponential-backoff ''' url = f"{Consts.API_SERVER_URI}/v2/writer-detector/source-code/{scan_id}/check" - return _AIDetectionClient.__submit(url, auth_token, scan_id, submission) \ No newline at end of file + return _AIDetectionClient.__submit(url, auth_token, scan_id, submission) From aa6b7f9df6a93e64fb175bc61b579e69c53a179f Mon Sep 17 00:00:00 2001 From: Elazarcl <elazarb@copyleaks.com> Date: Mon, 9 Sep 2024 16:45:45 +0300 Subject: [PATCH 3/3] add tests for ai detector and writing feedback add tests for ai detector and writing feedback --- example.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/example.py b/example.py index 2f770d0..b627bb5 100644 --- a/example.py +++ b/example.py @@ -26,9 +26,12 @@ import random from copyleaks.copyleaks import Copyleaks from copyleaks.exceptions.command_error import CommandError +from copyleaks.models.submit.ai_detection_document import NaturalLanguageDocument, SourceCodeDocument from copyleaks.models.submit.document import FileDocument, UrlDocument, OcrFileDocument from copyleaks.models.submit.properties.scan_properties import ScanProperties from copyleaks.models.export import * +from copyleaks.models.submit.score_weights import ScoreWeights +from copyleaks.models.submit.writing_assistant_document import WritingAssistantDocument # Register on https://api.copyleaks.com and grab your secret key (from the dashboard page). EMAIL_ADDRESS = 'your@email.addresss' KEY = '00000000-0000-0000-0000-000000000000' @@ -108,4 +111,50 @@ # # generate a pdf report: #pdf = Pdf() # Creating instance of Pdf. #pdf.set_create(True) # Setting the create pdf to True to generate PDF report. -#scan_properties.set_pdf(pdf) # Will generate PDF report. \ No newline at end of file +#scan_properties.set_pdf(pdf) # Will generate PDF report. + + +# This example is going to use the AI detector client to detect ai in text +sample_text = "Lions are social animals, living in groups called prides, typically consisting of several females, their offspring, and a few males. Female lions are the primary hunters, working together to catch prey. Lions are known for their strength, teamwork, and complex social structures." +natural_language_submission = NaturalLanguageDocument(sample_text) +natural_language_submission.set_sandbox(True) +response = Copyleaks.AiDetectionClient.submit_natural_language(auth_token, scan_id, natural_language_submission) +print(response) + + +# This example is going to use the AI detector client to detect ai in source code +sample_code = ( + "def add(a, b):\n" + " return a + b\n" + "\n" + "def multiply(a, b):\n" + " return a * b\n" + "\n" + "def main():\n" + " x = 5\n" + " y = 10\n" + " sum_result = add(x, y)\n" + " product_result = multiply(x, y)\n" + " print(f'Sum: {sum_result}')\n" + " print(f'Product: {product_result}')\n" + "\n" + "if __name__ == '__main__':\n" + " main()" +) +source_code_submission = SourceCodeDocument(sample_text, "example.py") +source_code_submission.set_sandbox(True) +response = Copyleaks.AiDetectionClient.submit_natural_language(auth_token, scan_id, source_code_submission) +print(response) + + +# This example is going to use the WritingAssistant client to get feedback on text +score_weight = ScoreWeights() +score_weight.set_grammar_score_weight(0.2) +score_weight.set_mechanics_score_weight(0.3) +score_weight.set_sentence_structure_score_weight(0.5) +score_weight.set_word_choice_score_weight(0.4) +submission = WritingAssistantDocument(sample_text) +submission.set_score(score_weight) +submission.set_sandbox(True) +response = Copyleaks.WritingAssistantClient.submit_text(auth_token, scan_id, submission) +print(response)